#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <cstdlib>
#include <new>
#include <typeinfo>
#include <list>
#include <thread> // std::thread
#include <mutex>  // std::mutex

using namespace std;

// 参考  https://www.cnblogs.com/harlanc/category/944583.html

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 1.
// Item 1 将c++视为一个语言联邦
// 如今的c++已经是一个多重泛型变成语言。支持过程化，面向对象，函数式，泛型和元编程的组合。这种强大使得c++无可匹敌，却也带来了一些问题。所有“合适的”规则看上去都有例外。我们怎样理解这样一门语言？

// c++ 是以c为基础的:区块(blocks), 语句(statements),预处理(pre-processor),内置数据类型(build-in data types),数组(array),指针(pointers)
// objected-oriented C++: class包括构造函数,析构函数,封装(encapsulation),继承(inheritance),多态(ploymorphism),虚函数(动态绑定)
// template c++ : 泛型编程和模板
// STL: template的函数库, 容器,迭代器,算法,函数对象

// 最容易的方法是不要将其看成单一的一门语言而是将其看成是一个有相关性的语言的联邦。在一个特定的子语言中，一些规则就比较简单，明确并且容易记忆。
// 当你从一个子语言切换到另外一个子语言时，这些规则可能会改变。为了更好的理解c++,你必须识别主要的子语言，幸运的是，只有四种子语言:
// c++以c语言为基础。块，声明，预处理，内建类型，数组，指针等等。都来自于c语言。在许多情况下，c++只不过提供了一些比c高级的解法，但是当你发现你自己正在使用c++中的c语言部分时，高效编程的规则反映出c语言更多的局限性：没有模板，没有异常处理，没有重载等等。
// 面向对象c++.c++这部分就是带类的c语言，主要包括：类（包括析构函数和构造函数），封装 ，继承，多态，虚函数（动态绑定）等等。
// 模板c++.这是c++的泛型编程部分，也是大多数程序员在经验方面所欠缺的，关于模板编程的设计遍及整c++编程，一些好的编程规则中包含只适用于模板的条款。事实上，模板编程如此强大，它带来了一个新的编程范型，模板元编程(TPM).
// STL.STL是一个模板库。它将容器，迭代器，算法和函数对象优雅的整合到一起，模板和库也可以用其他的方法构建出来。使用模板库有自己特殊的方式，当你在使用STL时，你必须遵循它的约定。
// 将这四种子语言记在心里，在不同的子语言之间进行切换时，高效编程可能需要变换策略，譬如，c语言中对于内建类型，按值传递比按引用传递要高效。在c++中，对于类对象来说，因为用户自定义的构造函数和析构函数的存在，按引用传递比按值传递要高效，模板c++中也是如此。但是在STL中，迭代器和函数对象是模仿的C语言中的指针，因此按值传递会比较高效。

// 因此c++不是统一标准的语言。它是一个语言联邦，每个子语言都有自己的约定



/////////////////////////////////////////////////////////////////////////////////////////
// 2.
// 尽量使用const,枚举(enums),内联(inlines),不要使用宏定义(define)
// 尽量使用编译器而不要使用预处理器更好。#define并没有当作语言本身的一部分

#define ASPECT_RATIO 1.63
// 符号名称永远不会被编译器看到。它可能在源码到达编译器之前被预处理器移除。ASPECT_RATIO 最终不会进入符号表，如果因为这个常量的使用而导致编译错误，会使你非常迷惑，因为错误信息会指向1.653而不是ASPECT_RATIO
// 如果ASPECT_RATIO被定义在一个不是你自己写的头文件中，你会不知道1.653来自哪里
// 解决方法是将宏替换成常量:
const double ASPECTRATIO = 1.63;

// 当用常量替换宏定义的时候，有两种特殊情况值得提一下:
// 第一种是定义常量指针，因为常量定义会被放到头文件中，很多文件会包含这个头文件，将指针声明成常量，同时将指针指向的内容也声明成常量。为了在头文件中定义一个基于char*的字符串，必须写const两次
const char *const authorname = "Scott";
// 在这里有必要提醒一下使用string对象要优于基于char*的字符串，所以将authorname定义成如下方式更好：
const std::string auther_name("Scott");

// 第二个值得注意的是class 专属常量,为了将常量的作用域 (scope) 限制于 class 内,你必须让它成为class 的一个成员 (member),而为确保此常量至多只有一份
// 实体,你必须让它成为一个static 成员
class costEstimate
{
private:
    static const double FudgeFactor; // 周期内一直存在,而且只有一份
};
const double costEstimate::FudgeFactor = 1.35;

// 你可以通过定义一个内联函数模板来获得宏定义函数所有的效率并且可预知函数的所有行为，函数也是类型安全的
template <typename T>
inline int callwithmax(const T &a, const T &b)
{
    return (a > b) ? a : b;
}

// 只要你写出 template inline函数,代替宏定义的函数


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 3. 在任何可能的时候使用 const
// Const 关键字是万能的，在类外部，你可以用它修饰全局的或者命名空间范围内的常量，也可以用它来修饰文件，函数和块作用域的静态常量。在类内部，你可以使用它来声明静态或者非静态的数据成员。
// 对于指针来说，你可以指定指针本身是不是const,指针指向的数据是不是const,两者可以同时为const或者两者同时为非const
char greeting[] = "Hello";        //non-const pointer non-const data
char *p_1 = greeting;             //non-const pointer non-const data
const char *p_2 = greeting;       //non-const pointer const data  // 左内, const 在*左边,内容为常量
char *const p_3 = greeting;       //const pointer non-const data
const char *const p_4 = greeting; //const pointer const data.
// 在指针中const的位置说明
// 这项语法并不像看上去那么反复无常，如果关键字const出现在星号的左侧，那么指针指向的内容为const,如果关键字const出现在星号的右侧，那么指针本身是const;如果星号出现在两侧，那么两者都为const.

// 当指针指向的内容为const时，一些程序员将const放在类型之前，一些程序员将const放在类型之后，星号之前，这两者在意义上是等同的，下面的两个函数有相同的参数类型:
class A
{
};
void f1(const A *pw);
void f2(A const *pw);

// const在迭代器中应用
// STL迭代器是模仿的指针，因此迭代器的行为非常像一个指针，声明一个迭代器的const就像声明一个指针的const(即声明一个T* const 指针),
//  迭代器不允许指向不同的东西，但迭代器指向的东西是可以改变的。
// 如果你想让迭代器指向的东西不能被修改，这时你就需要一个const_iterator.
std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin(); //iter acts like a T*const  // const位于*右边,因此是const pointer

// *iter = 10;//OK
// ++iter;//error
// std::vector<int>::const_iterator citer = vec.begin(); // citer acts like a T const *
// *citer = 10;//error
// ++citer;//OK

std::vector<int> vec_1{1, 2, 3, 4};
const std::vector<int>::iterator iter_1 = vec_1.begin(); //iter acts like a T*const
std::vector<int>::const_iterator citer = vec_1.begin();  //citer acts like a T const *

// Const修饰函数返回值和函数参数
// Const 的一些强大的用法来源于在函数声明上的应用。在一个函数声明中，const可以修饰函数返回值，函数参数，对于成员函数来说，可以修饰整个函数。
// 一个函数返回一个const值在一般情况下是不合适的。但有时在保证安全和效率的情况下可以减少客户端的出错率。举个例子，为有理数声明一个operator*函数。

// 有理数
class Rational
{
};
const Rational operator*(const Rational &lhs, const Rational &rhs);
// 为什么operator*的结果是一个const对象？因为如果不是const,客户端可以提交以下的暴行:
// Rational a,b,c;
// (a*b)=c;//invoke operator= on the result of a*b
// 我不知道为什么程序员会对两个数的乘积进行赋值，但我确实知道程序员虽然不想这么做，但确实这么做了。所有都是因为一个简单的输入错误（一个可以隐式转换成bool的类型)
// If(a*b=c)

// 如果a和b是内建类型，那么上面的代码是不合法的，一个好的用户自定义类型的特点是它们避免同内建类型的无端的不兼容（上面的代码如果内建类型不合法，那么用户自定义类型也应该不合法）
// 因此对两个数的乘积进行赋值也就没有理由这么做了,将operator*的返回值声明成const可以阻止这种赋值操作
// 对于const参数来说没有什么特别新的，它们就像local的const对象，你应该在任何可以使用它们的时候使用它。除非你像更改一个参数或者本地对象，否则确保它被声明成const,敲打六个字符的努力会可以使你免于类似上面的错误的打搅。

// Const 成员函数介绍
// 将const作用于成员函数上面的意图是能够确认哪些成员函数可以通过const对象被调用
// 这类成员函数很重要，原因有两条:
// 一，它们使得类的接口更容易被理解，知道哪些函数可以改变对象哪些不可以，这一点很重要。
// 二，它们使得和const对象一起工作成为可能，这是编写高效代码的很重要的方面，因为Item20解释道，提高c++程序性能的基本方法是按const引用传递对象。这项是可行的前提是，const成员函数能够处理const修饰的对象。

// 许多人忽略了一个事实，不同常量性的两个成员函数是可以重载的，这时c++的重要特性。考虑一个表现文本块的类:

class TextBlock_1
{
public:
    TextBlock_1(string test_input)
    {
        text = test_input;
    }

    const char &operator[](std::size_t positioin) const
    {
        return text[position];
    }

    char &operator[](std::size_t position)

    {
        return text[position];
    }

private:
    std::string text;
    int position;
};

// TextBlock 的operator[]函数可以像下面这样使用：
TextBlock_1 tb("hello");
// tb_1[0];
// std::cout << tb[0]; //call non-const operator[]

const TextBlock_1 ctb("World");
// std::cout << ctb[0]; //call const TextBlock::operator[]

// 顺便说一下，将指向const对象的指针或者指向const对象的引用作为参数经常出现在真实世界的程序中。上面的关于ctb的例子过于造作。下面的例子更符合实际:
void Print(const TextBlock_1 &ctb)
{
    std::cout << ctb[0]; //call const TextBlock::operator[]
}

// 通过重载operator[], 给不同的函数版本不同的返回值类型，可以对 const和非const TextBlocks进行不同处理：
// std::cout << tb[0]; // fine — reading a non-const TextBlock
// tb[0] = 'x'; // fine — writing a non-const TextBlock
// std::cout << ctb[0]; // fine — reading a const TextBlock
// ctb[0] = 'x'; // error! — writing a const TextBlock
// 注意这里错误的出现是由于调用operator[] 的返回值引起的，调用operator[] 本身没有问题。问题出现在尝试给一个const char &类型的变量进行赋值操作， const char &是const 版本的operator[] 的返回值类型。

//  同时需要注意 non-const operator[] 的返回值类型是指向char的引用，如果operator[] 返回一个char,
//     那么下面的句子将不能通过编译：
// tb[0] = 'x';
// 因为尝试修改内建类型的函数返回值是不合法的。即使是合法的，c++ 中的按值返回（Item20）意味着我们修改的是tb.text[0] 的拷贝，而不是tb.text[0] 本身，这不是你需要的行为。

// Const成员函数的两个派别

//         将成员函数声明成const意味这什么，对于这个问题有两种流行的见解：bitwise
//         constness(also known as physical constness) and
//     logical constness。

//         Bitwise const阵营相信当且仅当成员函数不修改对象的任何数据成员（不包括statics数据成员）时它才是const成员函数，举个例子：成员函数没有修改对象内部的任意bits.bitwise const的一个好处是能够比较容易的识别出反例：编译器只要寻找对数据成员的赋值就可以了。事实上，bitwise const是常量性的c++ 定义，const成员函数不允许修改对象的任何非静态数据成员。

//         不幸的是，许多成员函数没有表现的特别常量性，而不能通过bitwise
//         - const测试。特别的，一个成员函数修改指针指向的内容没有表现出常量性。但是如果指针是在对象内部，这个函数是bitwise const的，编译器不会发出抱怨。这会导致一个违反直觉的行为。举个例子，我们有个像testBlock的类，用char *而不是string来存储数据，因为它需要同一个不是识别string的C - API进行通讯


class CTextBlock
{

public:
    CTextBlock(char *test_input)
    {
        pText = test_input;
    }

    char &operator[](std::size_t position) const // inappropriate (but bitwise
    {
        return pText[position];
    } // const declaration of operator[]
// 这个 class 不适当地将其 operator[] 声明为 const 成员函数,而该函数却返回一个 reference 指向对象内部值
private:
    char *pText;
    int position;
};




//////////////////////////////////////////////////////////////////////////////////////
// Item 4 确保对象被使用前进行初始化

// 1. 使用未初始化对象的坏处
// 读取未初始化的值会产生未定义的行为。在一些平台中，仅仅读取未初始化的值就会让你的程序停止。更有可能读入一些半随机的bits,这会污染你的对象，最终导致不可思议的程序行为和很多不愉快的程序调试

// 2. 对于内建类型和非内建类型初始化的说明
// 一般来说，如果你使用c++中的c部分，初始化可能会招致运行期成本，因此不保证发生初始化。
// 如果你进入了c++的非C部分，事情就会发生改变。这就解释了为什么数组（来自c++的c语言部分）不保证它的内容被初始化，但是vector会保证(来自c++的STL部分)。

// 2.1. 如何保证内建类型进行初始化
// 处理这种看上去是不确定状态事务的最好方法是在你使用对象之前总是将它们进行初始化。对于内建类型的非成员对象，你需要手动初始化。举个例子:
// int x = 0; // manual initialization of an int
// const char * text = "A C-style string"; // manual initialization of a pointer (see also Item 3)
// double d; // “initialization” by reading from
// std::cin >> d; // an input stream

// 2.2. 如何保证非内建类型进行初始化
// 对于内置类型之外的其他东西，初始化的责任落在了构造函数身上。规则非常简单：确保所有的构造函数初始化对象的所有东西
// 别混淆了赋值(assignment) 和初始化( initialization) 
// 这个规则很容易遵守，但是不要将赋值和初始化搞混，这很重要。考虑一个表示地址簿的类，其构造函数如下:

class PhoneNumber
{
};

class ABEntry
{ // ABEntry = “Address Book Entry”

public:
    ABEntry(const std::string &name, const std::string &address,
            const std::list<PhoneNumber> &phones);

private:
    std::string theName;
    std::string theAddress;
    std::list<PhoneNumber> thePhones;
    int numTimesConsulted;
};

// // 赋值
// ABEntry::ABEntry(const std::string &name, const std::string &address,
//                  const std::list<PhoneNumber> &phones)
// {
//     theName = name; // these are all assignments, not initializations  // 赋值
//     theAddress = address;
//     thePhones = phones;
//     numTimesConsulted = 0;
// }

// 3.构造函数中的初始化列表和赋值 3.1. 使用初始化列表比赋值更具效率
// 这将会产生你所需要的ABEntry对象，但这仍然不是最好的方法。
// C++ 的规则中规定:对象的数据成员在进入构造函数体之前被初始化。在ABEntry构造函数内部，theName,
// theAdress和thePhones并没有被初始化，它们是被赋值。
// 初始化发生的更早，在进入ABEntry的构造函数体之前这些数据成员的默认构造函数会被自动调用。
// 但是这并不适用于numTimesConsulted, 因为它是内建类型。对于内建类型来说，不能够保证在赋值之前被初始化。

// 写出ABEntry构造函数的更好的方法是使用成员初始化列表，而不是赋值  // 初始化列表的效率更高
ABEntry::ABEntry(const std::string &name, const std::string &address,
                 const std::list<PhoneNumber> &phones)
    : theName(name),
      theAddress(address), // these are now all initializations
      thePhones(phones),
      numTimesConsulted(0)
{
} // the ctor body is now empty


// 最简单的做法就是:总是使用成员初值列。这样做有时候绝对必要,且又往往比赋值更高效。


// 这个构造函数会和上面的构造函数产生同样的结果。但是它会更有效率。
// 基于赋值的版本首先会调用默认构造函数来初始化theName,theAddress和thePhones,然后迅速的在默认构造出来的成员基础之上再进行赋值。
// 在默认构造函数中进行的所有工作因此被浪费了。成员初始化列表的使用避免了这个问题，因为成员初始化列表中的参数被用作不同数据成员的构造函数的参数。
// 在这种情况下，theName会以name作为参数拷贝构造出来，theAddress会以address作为参数拷贝构造出来，theAddress会以phones为参数拷贝构造出来。
// 对于大多数类型，比起先调用默认构造函数然后调用拷贝赋值运算符，调用一个单一拷贝构造函数是更有效率的，而且有时效率能够大大提高。

// 对于像numTImeConsulted这样的内建类型来说，初始化和赋值的开销是相同的，但是为了一致性，最好通过初始化列表对所有东西进行初始化。
// 类似的，在你想使用默认构造函数构造数据成员的时候，你仍然可以使用成员初始化列表:初始化参数不要指定任何东西。举个例子，如果ABEntry有一个构造函数不带任何参数，可以像下面这样来实现

// ABEntry::ABEntry():theName(),           // call theName’s default ctor;
//       theAddress(),        // do the same for theAddress;
//       thePhones(),         // and for thePhones;
//       numTimesConsulted(0) // but explicitly initialize
// {
// } // numTimesConsulted to zero

// 有时候成员初始化列表必须被使用，甚至对于内建类型也是这样。
// 举个例子，const数据成员或者引用数据成员必须被初始化而不能够被赋值(Item5)。为了避免需要记住什么时候数据成员必须被初始化什么时候是可选的，最简单的选择是总是使用初始化列表。
// 有时候这样做是需要的，它比赋值更加有效率

// 总之，真正的数据成员初始化（通过成员初始化列表）比“假的”通过赋值进行的初始化要更好。



//  4. 关于初始化顺序的说明
// 4.1.对于类对象成员初始化顺序的说明
// C++中一个不变的地方是对象的数据成员被初始化的顺序。这种顺序总是会相同的:基类在派生类初始化之前进行初始化(Item12),
// 在类内部，数据成员根据其在类中声明的顺序进行初始化。举个例子，在ABEntry中，theName首先被初始化，theAddress其次，thePhones第三个被初始化，numTimesConsulted最后被初始化，
// 即使这些数据成员在初始化列表中被列出的顺序不同（很不幸这是合法的），初始化顺序也是按照声明顺序。
// 为了防止读代码的人产生迷惑，同时为了防止一些模糊不清的错误，最好成员初始化列表中列出的数据成员顺序和声明顺序一致。



// 所谓 static 对象,其寿命从被构造出来直到程序结束为止,因此 stack 和
// heap-based 对象都被排除。这种对象包括 global 对象、定义于 namespace 作用域内
// 的对象、在 classes 内、在函数内、以及在 file 作用域内被声明为 static 的对象。
// 函数内的 static 对象称为 local static 对象(因为它们对函数而言是 local) ,其他 static
// 对象称为 non-local static 对象。程序结束时 static 对象会被自动销毁,也就是它们
// 的析构函数会在旧 main ()结束时被自动调用。

// 问题:
// 除非 tfs 在 tempDir 之前先被初始化,否则 tempDir 的构造函数会用到尚未初始化的 tfs

// 幸运的是一个小小的设计便可完全消除这个问题。唯一需要做的是:将每个
// non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为static) 。
// 这些函数返回一个 reference 指向它所含的对象。然后用户调用这些函数,而不直接
// 指涉这些对象。换句话说, non-local static 对象被 local static 对象替换了。 Design
// Pattems 迷哥迷姊们想必认出来了,这是 Singleton 模式的一个常见实现手法。
// 这个手法的基础在于: C++ 保证,函数内的 local static 对象会在"该函数被调
// 用期间" "首次遇上该对象之定义式"时被初始化。所以如果你以"函数调用" (返
// 回一个 reference 指向 local static 对象)替换"直接访问 non-local static 对象"

class FileSystem
{
};
FileSystem &tfs()
{
    static FileSystem fs;  // 定义并初始化一个 local_static 对象
    return fs;
}

class Directory
{
};
Directory::Directory(int params)
{
    std::size_t disks = tfs().fs.numDisks();
}




// 内置型对象进行手工初始化,因为 C++不保证初始化它们。
// ·构造函数最好使用成员初值列 (member initialization list) ,而不要在构造函数
// 本体内使用赋值操作(assignment) 。初值列列出的成员变量,其排列次序应该
// 和它们在 class 中的声明次序相同。
// ·为免除"跨编译单元之初始化次序"问题,请以local static 对象替换 non-local
// static 对象。


////////////////////////////////////////////////////////////////////////////
//  Item 5 了解c++默认生成并调用的函数
// 1 编译器会默认生成哪些函数
// 什么时候空类不再是一个空类？答案是用c++处理的空类。如果你自己不声明，编译器会为你声明它们自己版本的拷贝构造函数，拷贝赋值运算符和析构函数，如果你一个构造函数都没有声明，编译器同样会为你声明一个默认拷贝构造函数。
// 这些所有的函数会是public和inline的（Item30）。因此，如果你写了下面的类：

class Empty_1
{
};

class Empty
{
public:
    // 构造函数
    Empty() {} // default constructor

    // 拷贝构造函数
    Empty(const Empty &rhs)
    {
    } // copy constructor

    // 析构函数
    ~Empty() {} // destructor — see below
    // for whether it’s virtual

    // 拷贝赋值函数
    Empty &operator=(const Empty &rhs)
    {
    } // copy assignment operator
};

// 2.1 默认拷贝构造函数
//     对于拷贝构造函数和拷贝赋值运算符来说，编译器生成的版本只是将源对象的非静态数据成员简单的拷贝到目标对象上。例如，考虑一个NamedObject 模板类允许你同类型T的对象进行关联

template <typename T>
class NamedObject
{
public:
    NamedObject(const char *name, const T &value);
    NamedObject(const std::string &name, const T &value);

private:
    std::string nameValue;
    T objectValue;
};


//////////////////////////////////////////////////////////////////
// Item 6 如果你不想使用编译器自动生成的函数，你需要明确拒绝
// 问题描述-阻止对象的拷贝
// 为驳回编译器自动(暗自〉提供的机能,可将相应的成员函数声明为private 并且不予实现。使用像 Uncopyable 这样的 base class 也是一种做法。

// 现实生活中的房产中介卖房子，一个服务于这个中介的软件系统很自然的会有一个表示要被销售的房屋的类：

class HomeForSale
{
};

// 阻止对象拷贝方法一-将拷贝构造函数和赋值运算符声明为private,并且不去实现它们
// 解决方案的关键在于所有编译器生成的函数都是public的。为了阻止这些函数被生成，你必须自己声明它们，但是不需要你将它们声明成public的。
// 而是把拷贝构造函数和拷贝赋值运算符声明成private的。通过显示的声明一个函数，就会阻止编译器生成它们自己的版本，同时，通过将函数声明成private,你也能够阻止人来调用它们。
// 这个方法不是十分安全的，因为类成员函数和友元函数仍然能够访问你的私有函数。除非你够聪明而不去定义这些函数。
// 这样如果有人无意调用它们，就会得到一个链接错误。将函数声明成private而不去定义它的诡计如此被大家接受，它常常用在c++ 的iostreams库中，用于阻止类对象之间的拷贝。
// 你可以看一下，在你的标准库的实现中，ios_base,basic_ios和sentry的定义处。你就会发现在每种情况中，拷贝构造函数和拷贝赋值运算符都被声明成private的并且没有被定义。
// 将这个伎俩用于HomeSale很简单：

class HomeForSale_1
{

public:
private:
    // private 拷贝构造函数
    HomeForSale_1(const HomeForSale_1 &); // declarations only
    // private 拷贝赋值函数
    HomeForSale_1 &operator=(const HomeForSale_1 &);
};

// 阻止对象拷贝方法二-将函数的私有声明提到特定基类
// 将拷贝构造函数和拷贝赋值运算符声明为private的位置由HomeForSale类替换为一个专门设计的基类，同样能够阻止拷贝，
// 并且可以将链接才能发现的错误移动到在编译期就能够发现（这是好事，早点发现错误比晚发现要好）。

class Uncopyable
{
protected:           // allow construction
    Uncopyable() {}  // and destruction of
    ~Uncopyable() {} // derived objects...

private:
    // 拷贝构造函数
    Uncopyable(const Uncopyable &); // ...but prevent copying
    // 拷贝赋值函数
    Uncopyable &operator=(const Uncopyable &);
};
// 为了阻止HomeForSale被拷贝，我们需要做的是继承Uncopyable.

class HomeForSale_2 : private Uncopyable
{ // class no longer
    // declares copy ctor or

}; // copy assign. Operator

// 这个方法也是行得通的，因为编译器会尝试生成一个拷贝构造函数和一个拷贝赋值运算符如果任何人（包括成员函数或者友元函数）尝试拷贝一个HomeForSale对象。
// 在Item12中解释到，这些函数的编译器生成版本会调用基类的对应的部分，这样调用就会被拒绝，因为基类中的拷贝操作都是private的。

// Uncopyable的一些使用和实现有一些微妙的地方，像从Upcopyable继承不必是public继承（Item32和Item39）Uncopyable的析构函数不必是虚函数。
// 因为Uncopyable没有包含任何数据，因此符合Item39描述的，它是进行空基类优化(empty base class optimization) 的合格者.
// 一般来说，你可以忽略这些微妙之处，按照展示的使用就可以了，它会恰到好处的工作。你也可以使用Boost库中的版本，名字叫做noncopuable。这是一个很好的类，只是名字看上去不太自然。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Item 7 在多态基类中将析构函数声明为虚析构函数
// 1. 继承体系中关于对象释放遇到的问题描述
// 1.1 手动释放
// 关于时间记录有很多种方法，因此为不同的计时方法创建一个TimeKeeper基类和一些派生类就再合理不过了：

class TimeKeeper
{

public:
    TimeKeeper();
    ~TimeKeeper();
};

class AtomicClock : public TimeKeeper
{
};
class WaterClock : public TimeKeeper
{
};
class WristWatch : public TimeKeeper
{
};

// 许多客户端只想访问时间而不想知道关于时间计算的细节，所以可以创建一个工厂方法，这个工厂方法返回一个指向新创建的派生类对象的基类指针，这个指针用来指向一个计时对象:
TimeKeeper *getTimeKeeper(){}; // returns a pointer to a dynamic-ally allocated object of a class  // derived from TimeKeeper
// 为了和工厂方法的约定保持一致，getTimeKeeper返回一个堆上的对象，因此为了避免泄露内存和其他资源，每个返回的对象被合理的释放掉(deleted)是很重要的：

TimeKeeper *ptk = getTimeKeeper(); // get dynamically allocated object  // from TimeKeeper hierarchy
delete ptk; // release it to avoid resource leak

// Item13中解释到依赖客户执行deletion比较容易出错，在Item18中解释了如何改变工厂函数的接口来预防一般的客户端错误，但是这些关注点在这里都是次要的，
// 因为在这个条款中，我们为上面的代码提出一个更基本的弱点：即使客户端把一切都做对了，根本没有方法去知道程序如何运转。

// 1.2非虚析构函数引入的问题
// 问题在于getTimeKeeper返回一个指向派生类对象的指针(AtomicClock),这个对象通过一个基类指针(一个TimeKeeper*指针)来进行释放（delete），
// 基类中(TimeKeeper)有一个非虚析构函数。这是造成灾难的一个因素，
// 因为c++指出：通过一个基类的指针来释放一个派生类的对象，如果基类的析构函数是非虚的，那么结果未定义。
// 在运行时有可能发生以下状况：对象的派生类部分永远不会被释放掉。如果对getTimeKeeper的调用恰巧返回一个指向AtomicClock对象的指针，
// 对象的AtomicClock部分（也就是在AtomicClock类中声明的数据成员）可能不会被释放掉，AtomicClock类的析构函数也不会被执行。
// 然而，基类部分（也就是TimeKeeper部分）是会被释放掉的，这会导致产生一个古怪的“部分被释放的”对象。这是使资源泄露，破坏数据结构和在debugger上花费大把时间的绝佳方法。

// 回到顶部
// 2.如何解决问题-声明虚析构函数
// 消除这个问题很简单：为基类提供一个虚析构函数。这时如果delete一个派生类对象将会做到你想要的。它会释放掉整个对象，包括派生类的所有部分：

class TimeKeeper_1
{
public:
    TimeKeeper_1();
    virtual ~TimeKeeper_1();  // 虚的析构函数
};

TimeKeeper *ptk_1 = getTimeKeeper();
delete ptk; // now behaves correctly

// 基类中(TimeKeeper)除了析构函数外一般情况下会包含虚函数，因为虚函数存在的目的是为了函数在派生类中的定制化实现（Item34）。
// 举个例子，TimeKeeper会有一个虚函数，getCurrentTime,这个函数在不同的派生类中会有不同的实现。任何有虚函数的类应该肯定有一个虚析构函数。

// 如果类中不包含虚函数，这通常表明它不会被用作基类，如果并没有打算将一个类作为一个基类，将析构函数声明为虚是一个坏的想法。考虑一个表示二维空间的点的类 :

class Point
{ // a 2D point
public:
    Point(int xCoord, int yCoord);
    ~Point();

private:
    int x, y;
};

// 如果int占用32Bits, 那么一个Point对象可被放入一个64 - bit的缓存器中。并且，这个Point对象可以以一个”64 - bit quantity”传给用其他语言编写的函数，例如c语言和Fortran。
// 如果将Point的析构函数声明成虚的，状况就会发生变化。虚函数的实现需要对象带一些信息，根据这些信息在运行时能够决定对象的哪个虚函数会被触发。
// 这些信息表现为一个被叫做vptr(virtual table pointer) 指针的形式。我们把指向一个函数指针数组的vptr指针叫做vtbl(virtual table) ；
// 每个有虚函数的类都有一个关联的vtbl.当虚函数在一个对象上被触发，实际调用的函数是由对象的vtbl中的vptr来决定的，在vtbl中会查找到合适的函数指针。

// 关于虚函数是如何实现的细节并不重要。重要的是如果Point类中包含一个虚函数，这个类型的对象会在占用空间上有所增加：在32位机器中，空间会从64bits(两个int) 增加到96bits；
// 在64位机器中，空间会从64bits增加到128bits, 因为64位机器上的指针在空间上占用64bits.Point额外增加了一个vptr而致使内存空间增加50 - 100 %。Point将不能在放进64bits的缓存中。
// 并且，c++ 中的Point也不再同其他语言(如C语言) 中声明的对象有类似的结构了，因为其他语言没有vptr, 因此你不再能够向(从) 其他语言编写的函数中传进(传出) 指针了,
// 除非你对vptr进行明确的补偿，这属于实现细节，代码因此也不能够被移植了。 因此，无缘无故的将所有析构函数声明成虚函数同永远不将其声明为虚函数犯了一样的错误。
// 事实上，许多人将上面的情形其总结如下：在类中声明虚析构函数当且仅当类中至少包含一个虚函数。


// 析构函数的运作方式是,最深层派生(most derived) 的那个 class 其析构函数最先被调用,然后是其每一个base class 的析构函数被调用
// polymorphic (带多态性质的) base classes 应该声明一个 virtual 析构函数。如果class 带有任何 virtual 函数,它就应该拥有一个 virtual 析构函数。
// Classes 的设计目的如果不是作为 base classes 使用,或不是为了具备多态性(polymorphically) ,就不该声明 virtual 析构函数





//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 8 不要让异常(exceptions)离开析构函数

// 1.为什么c++不喜欢析构函数抛出异常
// C++并没有禁止析构函数出现异常，但是它肯定不鼓励这么做。这是有原因的，考虑下面的代码：

class Widget
{
public:
    ~Widget() {} // assume this might emit an exception
};

void doSomething()
{
    std::vector<Widget> v;
} // v is automatically destroyed here

// 当vector V被销毁，V有责任将它包含的所有Widgets都销毁。假设v含有有10个Widgets对象，当销毁第一个Widgets对象时，抛出了一个异常。
// 其余的9个仍然需要被释放掉（否则它们拥有的资源会被泄露），所以V应该触发其余9个对象所有的析构函数。但是假设在这9个析构函数调用过程中，
// 第二个Widget的析构函数抛出了一个异常。现在有两个主动抛出的异常了，这对c++来说太多了。在两个异常同时出现的情况下，
// 程序的执行要么终止要么产生未定义行为。在这个例子中，它会产生未定义行为。
// 使用任何其他标准库容器(如list或set)或者TR1中的容器，甚至一个数组也将会产生同样的未定义行为。出现这种麻烦并不只是在容器或者数组中出现。
// 在不使用容器或者数组的情况下，析构函数抛出的异常也可以使程序过早终止或者出现未定义行为。C++不喜欢析构函数发出异常！

// 2.一个例子-DB资源管理类
// 这很容易理解，但是析构函数需要执行的操作有可能由于异常被抛出而导致失败，这时候我们应该怎么做？举个例子，假设你在实现一个关于数据库连接的类：

class DBConnection
{

public:
    static DBConnection create(); // function to return  // DBConnection objects; params omitted for simplicity

    void close(){}; // close connection; throw an exception if closing fails
};

// 为了确保客户端不会忘记调用DBConnection对象的close函数，为DBConnestion创建一个资源管理类是一个理想的方法，close函数会在资源管理类的析构函数中被调用。
// 这样的资源管理类将在第三章有详细的讲述，在这里，考虑这样一个类的析构函数会长成什么样子就足够了：

class DBConn
{ // class to manage DBConnection

public: // objects make sure database connections are always closed
    ~DBConn();

private:
    DBConnection db;
};

DBConn::~DBConn()
{
    try
    {
        db.close();
    }

    catch (std::exception &e)
    {

        std::abort();
    }
}

// 如果在执行析构函数的时候遇到一个错误程序就不能继续运行了，上面的做法会是一个合理的选择。它的优点是能够阻止异常从析构函数传播出去，传播异常会导致未定义行为。因此，对于未定义行为，调用abort能够先发制人

// open a block

// 于是客户端代码可以写成这样：
// DBConn dbc(DBConnection::create()); // create DBConnection object

// 只要close函数的调用成功了这个实现就是很好的，但是如果调用产生一个异常，DBConn的析构函数会传播这个异常，也就是允许异常离开析构函数。
// 这是一个问题，因为在析构函数中发生throw就意味这麻烦。
// 3.如何阻止析构函数中的异常被传播出去
// 有两种方法来避免这个麻烦。DBConn的析构函数可以这么做：

// 3.1用abort函数使程序终止
// 如果close函数抛出异常就将程序终止，可以调用abort函数：




/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 9 绝不要在构造函数或者析构函数中调用虚函数
// 在构造和析构期间不要调用 virtual 函数,因为这类调用从不下降至 derived class (比起当前执行构造函数和析构函数的那层)
class Transaction
{ // base class for all transactions
public:
    Transaction(){};
    virtual void logTransaction() const = 0; // make type-dependent
    // log entry
};

// Transaction::Transaction() // implementation of base class ctor
// {
//     logTransaction(); // as final action, log this transaction
// }

class BuyTransaction : public Transaction
{ // derived class
public:
    virtual void logTransaction() const; // how to log trans-actions of this type
};

class SellTransaction : public Transaction
{ // derived class
public:
    virtual void logTransaction() const; // how to log trans-actions of this type
};

class Transaction_1
{

public:
    Transaction_1() { init(); } // call to non-virtual...
    virtual void logTransaction() const = 0;

private:
    void init()
    {
        logTransaction(); // ...that calls a virtual!
    }
};



class Transaction {
public:
explicit Transaction(const std::string& logInfo);
void logTransaction(const std::string& logInfo) const; // now a non-virtual func
};

Transaction::Transaction(const std::string& logInfo)
{
logTransaction(logInfo); // now a non-virtual call
}

class BuyTransaction: public Transaction {

public:

BuyTransaction( parameters )

: Transaction(createLogString( parameters )) // pass log info to base class constructor

{ }


// 换句话说由于你无法使用 virtual 函数从 base classes 向下调用,在构造期间,你
// 可以藉由"令 derived classes 将必要的构造信息向上传递至 base class 构造函数"替
// 换之而加以弥补

// private:
// // 注意BuyTransaction类中（private）静态函数createLogString的使用。使用一个helper函数来创建传递到基类构造函数的值比在成员初始化列表中提供基类需要的值更加方便（更加易读）
// 通过将此函数声明成static，就不会有引用BuyTransaction对象未初始化数据成员的危险（static函数只能够操作static数据成员）
// static std::string createLogString( parameters );

// std::string parameters
// };




////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Item 10 让赋值运算符返回指向*this的引用

/////////////////////////////////////////////////////////////////////////////////////////////
// Item 11 在operator=中处理自我赋值
// 1.自我赋值是如何发生的
// 当一个对象委派给自己的时候，自我赋值就会发生
// 一般情况下，当我们操作指向不同同类型对象的引用和指针时，需要考虑这些不同的对象是否是同一个对象。
// 事实上，如果两个对象来自同一个继承体系，这两个对象甚至不必声明为同类型的，因为基类的指针或者引用可以指向派生类对象

///////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 12 拷贝对象的所有部分
// 1.默认构造函数介绍
// 在设计良好的面向对象系统中，会将对象的内部进行封装，只有两个函数可以拷贝对象：拷贝构造函数和拷贝赋值运算符。我们把这两个函数统一叫做拷贝函数。
// 从Item5中，我们得知，如果需要的话编译器会为你生成这两个拷贝函数，并且编译器生成的版本能够精确的做到你想做的：它们拷贝了对象的所有数据。

// 2.1 问题出现场景一
// 考虑一个表示消费者的类，类中的拷贝函数已经被手动实现了，所以调用它们会被记入日志
// 结论很明显：如果你向类中添加一个数据成员，你需要确保同时对拷贝函数进行更新。（你同时需要更新类中所有的构造函数（Item4和Item45）和任何非标准形式的operator=(Item 10给出了一个例子)），如果你忘记了，编译器不会提醒你

void logCall(const std::string &funcName){}; // make a log entry

class Customer
{
public:
    Customer(const Customer &rhs);
    Customer &operator=(const Customer &rhs);

private:
    std::string name;
};

Customer::Customer(const Customer &rhs) : name(rhs.name) // copy rhs’s data
{
    logCall("Customer copy constructor");
}

Customer &Customer::operator=(const Customer &rhs)
{
    logCall("Customer copy assignment operator");
    name = rhs.name; // copy rhs’s data
    return *this;    // see Item 10
}

class PriorityCustomer : public Customer
{ // a derived class

public:
    PriorityCustomer(const PriorityCustomer &rhs);

    PriorityCustomer &operator=(const PriorityCustomer &rhs);

private:
    int priority;
};

// invoke base class copy ctor
// 拷贝构造函数
PriorityCustomer::PriorityCustomer(const PriorityCustomer &rhs) : Customer(rhs), priority(rhs.priority)
{
    logCall("PriorityCustomer copy constructor");
}

// 拷贝赋值运算符
PriorityCustomer &PriorityCustomer::operator=(const PriorityCustomer &rhs)
{
    logCall("PriorityCustomer copy assignment operator");
    Customer::operator=(rhs); // assign base class parts
    priority = rhs.priority;
    return *this;
}

// 在这个条款的标题中，“拷贝所有部分”的意思现在应该明了了。
// 当你实现一个拷贝函数的时候，确保（1）拷贝所有本地的数据成员。（2）同时调用所有基类的合适的拷贝函数。

///////////////////////////////////////////////////////////////////////////////////////////////////
// 通过对象来管理需要手动释放的资源
// 为了确保从createInvestment返回的资源总是被释放，我们需要将资源放到一个对象中，
// 当离开函数f的时候，对象的析构函数会自动释放对象拥有的资源。
// 事实上，我们已经说出了这个条款一半的内容：通过将资源放入对象中，我们可以依赖c++的析构函数自动调用机制来确保资源被释放。（另一半一会就会讲到）

// 2.1 使用auto_ptr来管理资源
// 许多资源是被动态的分配在堆上的，它们被用在一个单独的块或者函数中，当控制流离开块或者函数时，这些资源应该被释放。
// 标准库中的auto_ptr正是为这种情况量身定做的。Auto_ptr是一个指针（智能指针）一样的对象，它的析构函数会自动为其指向的对象调用delete函数。
// 下面演示如何使用auto_ptr来防止可能出现的资源泄露

class Investment
{
};
Investment *createInvestment(){}; // return ptr to dynamically allocated
void f()
{
    std::auto_ptr<Investment> pInv(createInvestment()); // call factory function use pInv as before
} // automatically delete pInv via auto_ptr’s dtor

// 2.2 用对象管理资源的两个关键点
// 这个简单的例子指出了使用对象管理资源的两个关键点：
// 获取资源后应该立即将其转交给资源管理对象。从上面的例子看出，使用createInvestment返回的资源来初始化对其进行管理的auto_ptr指针。
// 事实上，用对象来管理资源的想法通常被叫做”资源获取的时候就是初始化的时候”(Resource Acquisition Is Initialization RAII）,
// 因为将资源获取和资源管理对象的初始化放在同一个语句中是非常常见的。
// 有时用获取的资源给资源管理对象赋值而不是初始化，但是不管哪种方法，都是在资源获取到之后马上将控制权转交给资源管理对象。
// 资源管理对象使用它们的析构函数来确保资源被释放。因为不管控制流是怎么离开块或函数的，对象销毁的时候析构函数会被自动调用（例如当一个对象超出了作用域），
// 资源因此能够被正确释放。释放资源时抛出异常会使问题变的棘手，这个问题在Item8中讨论了，我们不再担心这种问题。
// 因为 当auto_ptr被销毁时会自动delete它所指向的资源，所以有没有多个auto_ptr指向通一个对象是很重要的。
// 如果有多个，对象会被多次delete,这就会导致出现未定义行为。为了防止这样的问题出现，
// auto_ptrs有一个与众不同的性质：被拷贝的指针（通过拷贝构造函数或者拷贝赋值运算符）会被置为null，进行拷贝的指针将拥有资源的所有权

// 2.3 用shared_ptr来管理资源
// 2.4 不要将auto_ptr和shared_ptr用于动态分配数组

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 14 对资源管理类的拷贝行为要谨慎
// Item 13中介绍了 “资源获取之时也是初始化之时（RAII）”的概念，这个概念被当作资源管理类的“脊柱“，也描述了auto_ptr和tr1::shared_ptr是如何用堆资源来表现这个概念的。
// 然而并不是所有资源都是在堆上创建的，对于这种资源，像auto_ptr和tr1::shared_ptr这样的智能指针就不适合当作资源句柄(handle)来使用了。
// 你会发现你时不时的就会需要创建自己的资源管理类。

// 举个例子，假设你正在使用C API来操纵Mutex类型的互斥信号量对象，来为函数提供lock和unlock：

void lock(mutex *pm);   // lock mutex pointed to by pm
void unlock(mutex *pm); // unlock the mutex

// 为了确保你不会忘记unlock一个已经加过锁的Mutex，你需要创建一个类来管理锁。这样一个类的基本结构已经由RAII准则表述过了，也就是资源会在执行构造的时候获取到，
// 在执行析构的时候释放掉:
class Lock
{
public:
    explicit Lock(mutex *pm) : mutexPtr(pm) { lock(mutexPtr); } // acquire resource
    ~Lock() { unlock(mutexPtr); }                               // release resource
private:
    mutex *mutexPtr;
};

// 客户端以传统的RAII方式来使用锁：
mutex *m; // define the mutex you need to use
          // create block to define critical section

// Lock ml(m); // lock the mutex perform critical section operations

// automatically unlock mutex at end of block

// 2. 对资源管理类进行拷贝会发生什么？
//     这很好，但如果一个锁对象被拷贝会发生什么呢？

// Lock ml1(&m);  // lock m
// Lock ml2(ml1); // copy ml1 to ml2 — what should

// 2.1 禁止拷贝
//     禁止拷贝。在许多情况下，允许RAII对象被拷贝是没有意义的。对于一个像Lock的类来说这可能是真的，因为一份同步原语(synchronization primitives) 的拷贝很少情况下是有意义的。
//当一个RAII类的拷贝没有意义时，你应该禁止它。Item 6解释了如何可以做到：将拷贝操作声明称private。对于Lock来说，可以是下面这个样子：

/////////////////////////////////////////////////////////////////////////////////////////////////
// Item 15 在资源管理类中提供对原生(raw)资源的访问

// 2. 如何获取原生资源——通过显示转换和隐式转换
// 2.1 一个例子
// 举个例子，Item 13中介绍了使用像auto_ptr或者tr1::shared_ptr这样的智能指针来存放调用createInvestment工厂函数的返回结果:

std::shared_ptr<Investment> pInv(createInvestment()); // from Item 13

// 假设你想使用一个同Investment对象一起工作的函数，如下：

int daysHeld(const Investment *pi){}; // return number of days
                                      // investment has been held

// 你会像下面这样调用它:
// int days = daysHeld(pInv); // error!

// 代码将不能通过编译：dayHeld想要使用一个原生Investment*指针，你却传递了一个tr1::shared_ptr<Investment>类型的对象。

// 你需要一种方法将一个RAII类对象（在这个例子中是tr1::shared_ptr）转换成它所包含的原生资源类型。有两种常见的方法来实现它：显示转换和隐式转换。

// 2.2 使用智能指针的get进行显示转换
// Tr1::shared_ptr和auto_ptr都提供了一个get成员函数来执行显示转换，也就是返回智能指针对象内部的原生指针：

int days = daysHeld(pInv.get()); // fine, passes the raw pointer

// in pInv to daysHeld

///////////////////////////////////////////////////////////////////////////////////////////
// Item 16 成对使用new和delete时要用相同的形式
// 规则很简单：如果你在一个new表达式中使用”[]”，你必须在对应的delete表达式中使用”[]”，反之亦然。

///////////////////////////////////////////////////////////////////////////////////////////
//  Item 17 使用单独语句将new出来的对象放入智能指针
// 可能会出现资源泄漏的一种用法
// 假设我们有一个获取进程优先权的函数，还有一个在动态分配的Widget对象上根据进程优先权进行一些操作的函数：

class AA
{
};

int priority();
void processWidget(std::shared_ptr<AA> pw){};
// 注意这里使用了对象管理资源的用法(Item 13),processWidget为它需要处理的动态分配对象Widget使用了智能指针(tr1::shared_ptr)
// 回到顶部
// 3. 如何避免资源泄漏
// 防止这个问题的方法比较简单：使用一个单独的句子创建Widget并将其存入智能指针，然后将智能指针传入processWidget：

std::shared_ptr<AA> pw(new AA); // store newed object
                                // in a smart pointer in a

// standalone statement

// processWidget(pw); // this call won’t leak

// 这种方法是行得通的，编译器被给予更少的余地来对语句进行重新排序。在上面的代码中，我们将“new Widget”以及对tr1::shared_ptr构造函数的调用放在一个语句中，把对priority的调用放在另一个语句中，这样就不允许编译器在”new Priority”和tr1::shared_ptr构造函数之间执行priority。

// 回到顶部
// 4. 总结
// 在智能指针中存储new出来的对象时要用单独的语句，不然抛出异常的时候会发生微妙的资源泄漏。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 18 使接口容易被正确使用，不容易被误用

// 2. 编写好的接口的方法列举
// 2.1 使接口不容易被误用——通过引入新的类型
// 开发出容易被正确使用不容易被误用的接口需要你考虑客户可能出现的所有类型的错误。举个例子，假设你正在为一个表示日期的类设计一个构造函数：

class Date_1
{
public:
    Date_1(int month, int day, int year){};
};

// 乍一看，这个接口可能看上去去合理的，但是客户很容易犯至少两种错误。

// 第一，他们可能搞错参数的传递顺序：
Date_1 d_1(30, 3, 1995); // Oops! Should be “3, 30” , not “30, 3”
                         //  第二，他们可能传递一个无效的月份或者天数(day number)：
Date_1 d_2(3, 40, 1995); // Oops! Should be “3, 30” , not “3, 40”
// （最后一个例子看上去很病态，但是不要忘了在键盘上，数字4和3是挨着的，将3错打成4这样的错误不是不常见。）

// 通过引入新的类型，许多客户错误就能被避免。确实，类型系统（type system）是你阻止不合要求的代码编译通过的主要盟友。
// 在这种情况下，我们可以引入简单的包装类型来区分天，月和年，然后在Date构造函数中使用这些类型：

struct Day
{
    explicit Day(int d) : val(d) {}
    int val;
};
// struct Month
// {
//     explicit Month(int m) : val(m) {}
//     int val;
// };
struct Year
{
    explicit Year(int y) : val(y) {}
    int val;
};

// class Date
// {
// public:
//     Date(const Month &m, const Day &d, const Year &y){};
// };
// // Date d(30, 3, 1995);                   // error! wrong types
// // Date d(Day(30), Month(3), Year(1995)); // error! wrong types
// Date d(Month(3), Day(30), Year(1995)); // okay, types are correct

// 将Day, Month和Year数据封装在羽翼丰满的类中比上面简单的使用struct要更好(Item 22) ，但是使用struct就足以证明，明智的引入新类型可以很好的阻止接口被误用的问题。
// 一旦正确的类型准备好了，就能够合理的约束这些类型的值。举个例子，只有12个月份应该能够通过Month类型反映出来。一种方法是使用一个枚举类型来表示月份，但是枚举不是我们喜欢的类型安全的类型。
// 例如，枚举可以像int一样使用（Item 2）。一个更加安全的解决方案是预先将所有有效的月份都定义出来。

class Month
{

public:
    static Month Jan() { return Month(1); } // functions returning all valid

    static Month Feb() { return Month(2); } // Month values; see below for why these are functions, not

    static Month Dec()
    {
        return Month(12);
    } // objects

    // other member functions

private:
    explicit Month(int m){}; // prevent creation of new Month values month-specific data
};

class Date
{
public:
    Date(const Month &m, const Day &d, const Year &y){};
};

Date d(Month::Dec(), Day(30), Year(1995));

// 如果使用函数代替对象来表示指定月份值会让你觉的奇怪的话，可能是因为你忘记了非本地static对象的初始化是有问题的（见 Item 4）。

// 2.2 使接口不容易被误用——对类型的操作进行限定
// 另外一种防止类似错误的方法是对类型能够做什么进行限制。进行限制的一般方法是添加const。举个例子，Item 3解释了对于用户自定义的类型，把operator*的返回类型加上const能够防止下面错误的发生：
// 1 if (a * b = c) ... // oops, meant to do a comparison!






///////////////////////////////////////////////////////
// Item 19 像设计类型（type）一样设计类

// 新 type 的对象应该如何被创建和销毁?这会影响到你的class 的构造函数和析构函
// 数以及内存分配函数和释放函数(operator new , operator new [l; operator delete
// 和 operator delete[]

// 新 type 的对象如果被passed by value ( 以值传递) ,意味着什么?记住 , copy 构造
// 函数用来定义一个 type 的 pass-by-value 该如何实现。






//////////////////////////////////////
//  Item 20 优先使用按const-引用传递（by-reference-to-const）而不是按值传递(by value)
// 1. 按值传递参数会有效率问题
// 默认情况下，C++向函数传入或者从函数传出对象都是按值传递（pass by value）(从C继承过来的典型特性)。除非你指定其他方式，函数参数会用实际参数值的拷贝进行初始化，函数调用者会获得函数返回值的一份拷贝。
// 这些拷贝由对象的拷贝构造函数生成。这使得按值传递（pass-by-value）变成一项昂贵的操作。举个例子，考虑下面的类继承体系(Item 7)

class Person
{
public:
    Person(){};          // parameters omitted for simplicity
    virtual ~Person(){}; // see Item 7 for why this is virtual
private:
    std::string name;
    std::string address;
};

class Student : public Person
{
public:
    Student(){}; // parameters again omitted
    virtual ~Student(){};

private:
    std::string schoolName;
    std::string schoolAddress;
};

// 现在考虑下面的代码，在这里我们调用了一个函数，validateStudent，这个函数有一个Student参数（按值），返回值表示验证是否通过：
bool validateStudent(Student s){}; // function taking a Student

// by value
Student plato;                           // Plato studied under Socrates
bool platoIsOK = validateStudent(plato); // call the function
// 当函数被调用时会发生什么？
// 很清楚，Student拷贝构造函数会被调用，用plato来初始化参数s。同样很清楚的是，当validateStudent函数返回后s会被销毁。所以这个函数参数传递的开销是分别调用了构造函数和析构函数
// →次 Student copy构造函数调用,加上一次Student 析构函数调用
// 但这不是所有的开销。一个Student对象中有两个string对象，所以每次你构建一个Student对象的时候你必须构造两个string对象。
// Student对象继承自Person对象，所以每次你构建一个Student对象你必须构造一个Person对象。一个Person对象中有两个额外的string对象，
// 所以每个Person构造函数同样需要对两个额外的string进行构造。最后结果是按值传递一个Student对象导致对Student拷贝构造函数的一次调用，对Person拷贝构造函数的一次调用，对stirng拷贝构造函数的四次调用。
// 当Student对象的拷贝被释放时，每个构造函数对应的析构函数要被调用，所以按值传递一个Student对象的总开销是6次构造和6次析构！！

// 2. 按const引用传递会更高效
// 这是正确的并且令人满意的行为。毕竟，你需要的是所有对象被可靠的初始化和销毁。并且，如果有一种方法能够绕过这些构造函数和析构函数就再好不过了
// 这种方法是存在的，就是：按const引用进行传递（pass by reference-to-const）

bool validateStudent(const Student &s);

// 这种用法更具效率：没有构造函数或者析构函数被调用，因为没有新的对象被创建。在修订后版本的参数声明中，const是很重要的。validataStudent的原始版本有一个按值传递的Studetn参数，
// 调用者会知道对被传递进去的Student参数的任何可能的修改都会被屏蔽掉,validateStudent只是在修改它的一份拷贝
// 现在Student被按照引用进行传递，将其声明为const同样是必须的，否则调用者就会为传递进去的参数是否被修改而担心

// 3. 按const引用传递能避免切片问题
// 按引用传递参数同样避免了切片（slicing）问题。当一个派生类对象被当作一个基类对象被传递时(按值传递),基类的拷贝构造函数会被调用，“使对象的行为看起来像派生类对象“这个特定的特性被“切掉”了。
// 留给你的只剩下一个基类对象，因为是一个基类的构造函数创建了它。这是你永远不希望看到的。举个例子，假设你正在一些类上进行工作，这些类实现了图形化窗口系统：

class Window
{
public:
    std::string name() const {};     // return name of window
    virtual void display() const {}; // draw window and contents
};

class WindowWithScrollBars : public Window
{
public:
    virtual void display() const {};
};

// 所有的窗口对象都有一个名字，你可以通过name函数来获取它，并且所有的窗口都能被显示出来，你可以通过触发display函数来实现。
// Display函数为虚函数的事实告诉你基类Windows对象的显示方式同WindowWithScrollBars对象的显示方式是不同的（Item 34和Item 36）
// 现在假设你实现了一个函数，先打印窗口的名字然后让窗口显示出来。下面是实现这样一个函数的错误的方式：

void printNameAndDisplay(Window w) // incorrect! parameter may be sliced!
{
    std::cout << w.name();
    w.display();
}

// 考虑当你使用一个WindowWithScrollBars对象作为参数调用这个函数会发生什么:
WindowWithScrollBars wwsb;
//  printNameAndDisplay(wwsb);

// 参数w将会被构造，它是按值传递的，所以w作为一个Window对象，所有让wwsb看起来像一个WIndowWithScrollBars对象的特定信息都会被切除
// 在printNameAndDispay内部，w的行为总是会像Window对象一样（因为他是一个Window类的对象），而不管传入函数的参数类型是什么
// 特别的，在printNameAndDisplay内部对display的调用总是会调用Window::display，永远不会调用WindowWithScrollBars::display

// 解决切片问题的方法是将w按const引用传递进去（by reference-to-const）:

void printNameAndDisplay(const Window &w) // fine, parameter won’t be sliced  // 现在w的行为会和传入参数的实际类型一致了
{
    std::cout << w.name();
    w.display();
}

// 4. 什么情况下按值传递是合理的
// 如果你偷看一下C++编译器的底层，你将会发现引用是按照指针来进行实现的，所以按引用传递一些东西就意味着传递一个指针。因此，如果你有一个内建类型的对象（例如int）按值传递比按引用传递效率更高。
// 对于内建类型来说，当你在按值传递和按引用传递之间进行选择时，选择按值传递是合理的。这对于STL中的迭代器和函数对象同样适用，因为按照惯例，它们被设计成按值传递。
// 迭代器和函数对象的设计者有责任留意下面两个问题：高效的拷贝和不用忍受切片问题。（这是一个规则如何被改变的例子，取决于你使用C++的哪一部分 见 Item 1。）

// 5. 并不是对象小就应该按值传递
// 内建类型占用了很少的内存，所以一些人得出结论：所有这样的小的类型都是按值传递的候选者，即使它们是用户定义的类型。这个原因是靠不住的。因为一个对象占用内存少并不意味这调用它的拷贝构造函数不昂贵。
// 许多对象——这些对象中的大多数STL容器——仅仅包含一个指针，但是拷贝这些对象会拷贝它们指向的所有东西。这可是非常昂贵的操作。
// 即使是当小对象的拷贝构造函数的调用开销很小时，也会有性能问题。一些编译器对于内建类型和用户自定义类型有不同的对待方式，即使它们有相同的底层表示(underlying representation）
// 举个例子，一些编译器拒绝将只含有一个double数值的对象放入缓存中，却很高兴的为一个赤裸裸的double这么做。当这类事情发生的时候，将这些对象按引用传递会更好，因为编译器会将指针（引用的实现）放入缓存中

// 另外一个小的用户自定义类型不是按值传递的好的候选者的原因是，作为用户自定义类型，它们的大小会发生变化。一个类型现在可能很小但是在将来的发布中可能会变的更大，因为它的内部实现可能发生变化。
// 当你切换到一个不同的C++实现时事情也有可能发生变化。举个例子，标准库的string类型的一些实现比其他实现大6倍。

// 一般情况下，你能够对“按值传递是不昂贵的”进行合理假设的唯一类型就是内建类型和STL迭代器以及函数对象。对于其它的任何类型，遵循这个条款的建议，优先使用按const引用传递而不是按值传递。

// 6. 总结
// 优先使用按const-引用传递而不是按值传递。它更具效率并且能够避免切片问题。
// 这个规则不适用于内建类型，STL迭代器和函数对象类型。对于它们来说，按值传递通常是合适的。




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 21 当你必须返回一个对象的时候，不要尝试返回引用

// 1. 问题的提出：要求函数返回对象时，可以返回引用么？
// 一旦程序员理解了按值传递有可能存在效率问题之后（Item 20）,许多人都成了十字军战士，决心清除所有隐藏的按值传递所引起的开销。对纯净的按引用传递（不需要额外的构造或者析构）的追求丝毫没有懈怠，
// 但他们的始终如一会产生致命的错误：它们开始传递指向并不存在的对象的引用。这可不是好事情。

// 考虑表示有理数的一个类，它包含将两个有理数相乘的函数(Item 3):
class Rational_1
{
public:
    Rational_1(int numerator = 0, // see Item 24 for why this ctor isn’t declared explicit
               int denominator = 1){};

private:
    int n, d;                                                                          // numerator and denominator
    friend const Rational_1 operator*(const Rational_1 &lhs, const Rational_1 &rhs){}; // // see Item 3 for why the return type is const
};

// Operator* 的这个版本为按值返回结果，如果你没有为调用这个对象的构造函数和析构函数造成的开销而担心，你就是在逃避你的专业职责。如果这个对象不是必须的，你就不想为这样一个对象的开销去买单。所以问题是：这个对象的生成是必须的么？

// 2. 问题的分析（一）：如返回引用，必须为返回的引用创建一个新的对象
//     如果你能够返回一个引用那么就不是必须为其买单。但是记住引用只是一个别名，一个已存对象的别名。每当你声明一个引用时，你应该马上问问自己它用来做谁的别名，因为它必须是某些东西的别名。 对于operator *来说，如果这个函数返回一个引用，它必须返回一个指向已存在Rational对象的引用，这个对象包含了两个对象的乘积结果。 没有任何理由假设在调用operator *之前这样一个对象已经存在了。也就是说，如果你进行下面的操作：

Rational_1 a(1, 2);   // a = 1/2
Rational_1 b(3, 5);   // b = 3/5
Rational_1 c = a * b; // c should be 3/10
                      // 期望已经存在一个值为3 / 10的有理数看上去是不合理的。如果operator *即将返回一个指向值为3 / 10的有理数的引用，它必须自己创建出来。

// 3. 问题的分析（二）：创建新对象的三种错误方法
// 3.1 在栈上创建reference指向的对象
// 一个函数只可以通过两种方法来创建一个新的对象:在栈上或者在堆上。通过定义一个本地变量来完成栈上的对象创建。使用这个策略，你可以尝试使用下面的方法来实现：operator*:

// const Rational &operator*(const Rational &lhs, const Rational &rhs) // warning! bad code!
// {
//     Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
//     return result;
// }

// 你会立即否决这种做法，因为你的目标是避免调用构造函数，但是这里的result必须被构造出来。更加严重的问题是：这个函数返回指向result的引用，但result是一个本地对象，
// 当函数退出的时候这个对象就会被销毁。所以这个版本的operator *并没有返回指向Rational的引用，它返回的引用指向从前的Rational对象，现在变成了一个空的，令人讨厌的,
// 已经腐烂的Rational对象的尸体, 它已经被销毁了。任何使用这个函数的返回值的调用者都将会马上进入未定义行为的范围。事实是，任何返回指向本地对象的引用的函数都是被破坏掉的函数。（返回指向本地对象的指针的函数也是如此）。

// 3.2 在堆上创建reference指向的对象 让我们再考虑一下下面这种用法的可能性：在堆上创建一个对象并且返回指向它的引用。堆上的对象通过使用new来创建，所以你可以像下面这样实现一个基于堆的operator * :

// const Rational & operator*(const Rational &lhs, const Rational &rhs) // warning! more bad code!
// {
//     Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
//     return *result;
// }

// 这里你仍然需要为构造函数的调用买单，对new分配的内存进行初始化是通过调用一个合适的构造函数来实现的，但是现在有另外一个问题：谁在这个对象上应用new召唤出来的delete ?
// 即使是一个认真负责的，心怀善意的调用者，对于下面这种合理的使用场景，他们也没有什么方法来避免内存泄漏：

// Rational w,x, y, z;
// w = x * y * z; // same as operator*(operator*(x, y), z)

// 这里，在同一个语句中调用了两次operator *，因此使用了两次new，这也需要使用两次delete来对new出来的对象进行销毁。没有什么合理的方法来让operator *的客户来进行这些调用，
// 因为对于他们来说没有合理的方法来获取隐藏在从operator *返回回来的引用后面的指针。这么做保证会产生资源泄漏。

// 3.3 为reference创建 static对象 3.3.1单一static 对象
//     你可能注意到了，不管是在堆上还是栈上创建从operator *返回的结果，你都必须要调用一个构造函数。可能你能回忆起来我们的初衷是避免这样的构造函数调用。可能你认为你知道一种只需要调用一次构造函数，
//         其余的构造函数被避免调用的方法。下面的这种实现突然出现了，这种方法基于另外一种operator *的实现：令其返回指向static Rational对象的引用，函数实现如下：

// const Rational &operator*(const Rational &lhs, const Rational &rhs)   // warning! yet more bad code!
// {
//     static Rational result; // static object to which a reference will be returned
//     // result = // multiply lhs by rhs and put the product inside result
//     return result;
// }

// 4. 问题结论：从函数中返回新对象的正确方法是——返回对象
//     实现一个必须返回一个新对象的函数的正确方法是让函数返回新的对象（value不是reference）。对于Rational的opertaor *函数来说，其实现如下面的代码（或者与其等价的代码）：

class Rational_2
{
public:
    Rational_2(int numerator = 0, // see Item 24 for why this ctor isn’t declared explicit
               int denominator = 1){};

    int n, d; // numerator and denominator
    // friend const Rational_2 operator*(const Rational_2 &lhs, const Rational_2 &rhs){}; // // see Item 3 for why the return type is const
};

inline const Rational_2 operator*(const Rational_2 &lhs, const Rational_2 &rhs)
{
    return Rational_2(lhs.n * rhs.n, lhs.d * rhs.d);
}

// 当然，你会从operator*的返回值中引入构造和析构的开销，但从长远来看，这是为正确的行为付出了一个小的代价。此外，让你毛骨悚然的账单再也不会到来。像许多编程语言一样，
// C++允许编译器实现者在不改变可视化代码行为的前提下，对代码进行优化，以达到改善生成码性能的目的。在一些情况中，我们发现,operator*返回值的构造和析构可以被安全的消除。当编译器利用了这个事实（编译器经常这么做），
// 你的程序就会以你所期望的方式进行下去，只是比你想要的要快。

// 将本条款归结如下：在返回一个引用还是返回一个对象之间做决定时，你的工作是选择能够提供正确行为的那个。对于“如何使这个选择有尽可能小的开销”这个问题的解决，让编译器供应商去斗争把。

// 回到顶部
// 5. 总结
// 绝不要返回指向本地栈对象的指针或者引用，指向堆对象的引用，或者在有可能需要多个对象的时候返回指向本地静态对象的指针或者引用。(Item 4)给出了一种设计的一个例子，说明了返回指向本地静态对象的引用是合理的，至少在单线程环境中。)
// 绝不要返回 pointer 或 reference 指向一个 local stack 对象,或返回 reference 指向一个 heap-allocated对象,或返回pointer 或 reference 指向一个 local static 对象而有可能同时需要多个这样的对象






/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Item 22 将数据成员声明成private

// 1. 为什么数据成员不能是public的?
// 为什么数据成员不能够是public的？

// 2.1 一致性
// 让我们从句法的一致性开始（Item 18）。如果数据成员不是Public的，那么客户访问对象的唯一方法就是通过成员函数。如果所有的公共接口都是函数，客户就不必记住访问一个类的成员时是否使用括号了。这方便了客户的使用。

// 2.2 对数据成员访问的精确控制
// 如果一致性没有让你信服，那么使用函数可以使你对数据成员的访问有更加精确的控制呢？如果你将数据成员声明成public的，每个人对其都有读写权限，但是如果你使用函数来对值进行获取（get）或者设置(set)，
// 你就可以实现不可访问（no access），只读访问(read only)和读写(read-write)访问。如果你需要，你甚至可以实现只写(write-only)访问:

class AccessLevels
{
public:
    int getReadOnly() const { return readOnly; }
    void setReadWrite(int value) { readWrite = value; }
    int getReadWrite() const { return readWrite; }
    void setWriteOnly(int value) { writeOnly = value; }

private:
    int noAccess; // no access to this int

    int readOnly; // read-only access to this int

    int readWrite; // read-write access to this int

    int writeOnly; // write-only access to this int
};

// 这种细粒度的访问控制是很重要的，因为许多数据成员应该被隐藏起来。很少情况下需要所有的数据成员都有一个getter和一个setter。

// 2.3 封装
// 仍然没有说服力？该是使出杀手锏的时候了：封装。如果你通过一个函数来实现对一个数据成员的访问，日后你可能会用计算来替代数据成员，使用你的类的任何客户不会觉察出类的变化。

// 举个例子，假设你在实现一个应用，自动化设备使用这个应用来记录通过车辆的速度。当每辆车通过的时候，速度被计算出来，然后将结果保存在一个数据集中，这个数据集记录了迄今为止收集的所有速度数据：

class SpeedDataCollection
{
public:
    void addValue(int speed);    // add a new data value
    double averageSoFar() const; // return average speed
};

// 现在考虑成员函数averageSoFar的实现。一种实现的方法是在类中定义一个数据成员，用来表示迄今为止所有速度数据的平均值。当averageSoFar被调用的时候，它只是返回这个数据成员的值。
// 另外一种方法是在每次调用averageSoFar的时候重新计算平均值，这可以通过检查数据集中的每个数据值来做到。

// 第一种方法使得每个SpeedDataCollection对象变大，因为你必须为保存平均速度，累积总量以及数据点数量的数据成员分配空间。然而，averageSoFar可以被很高效的实现出来；
// 它只是一个返回平均速度的内联函数（见Item 30）。相反，在请求的时候才计算平均值会使得averageSoFar运行非常缓慢，但是每个SpeedDataCollection对象会比较小。

// 谁能确定哪个才是更好的呢？在一台内存吃紧的机器上，并且应用中对平均值的需要不是很频繁，每次计算平均值可能会是一个更好的选择。在一个对平均值需求频繁的应用中，速度很重要，但内存充足，
// 你可能更喜欢将平均速度保存为数据成员。这里的重要一点是通过一个成员函数来访问平均值(也就是将其封装起来)，你可以在这些不同实现之间来回切换，客户端至多只需要重新编译就可以了。（通过Item31中描述的技术，你甚至可以不用重新编译）

// 将数据成员隐藏在函数接口后边可以灵活的提供不同种类的实现。举个例子，它可以使下面这些实现变得很简单：当数据成员被读或者写的时候通知其它对象；
// 验证类的不变性和函数的先置和后置条件；在多线程环境中执行同步等等。从其它语言（像Delphi和C#）转到C++的程序员将会识别出来C++的这种功能同其它语言中的“属性”是等同的，但是需要额外加一对括号。

// 封装比它起初看起来要重要。如果你对客户隐藏你的数据成员（也就是封装它们），你就能够确保类能一直维持不变性，因为只有成员函数能够影响它们。进一步来说，你保留了日后对实现决策进行变动的权利。
// 如果你没有将这些决策隐藏起来，你将会很快发现即使你拥有一个类的源码，但是你修改public成员的能力是及其受限的，因为如果修改public成员，太多的客户代码会被破坏。Public意味这没有封装，
// 更实际的讲，未封装意味这不能变化，特别对被广泛使用的类更是如此。因此对广泛使用的类最需要进行封装，因为它们最能受益于将一个实现替换为一个更好的实现。

// 回到顶部
// 2. 为什么数据成员不能是protected的？
// 上面的论证对于protected数据成员来说是类似的。事实上，它们是完全相同的，虽然一开始看上去不是这样。在论证数据成员不能为public时，句法一致性和细粒度访问控制这两个原因同样适用于protected成员，
// 但是封装呢？protected数据成员不是有比public数据成员更好的封装性么？令人感到吃惊的回答是，它们不是。

// Item 23解释了封装性同一些东西发生变化引起的代码可能被破坏的数量成反比。一个数据成员的封装型，同数据成员发生变化引起的代码可能被破坏的数量成反比，
// 举个例子，如果数据成员从类中移除。（可能被一个计算代替，正如在averageSoFar中实现的）。

// 假设我们有一个public数据成员，我们将其删除。有多少代码会被破坏？所有使用它的客户代码将被破坏，一般情况下这应该是个未知的数量。Public数据成员因此完全没有被封装。
// 但是假设我们有一个protected数据成员，我们将其删除。现在会有多少代码被破坏呢？所有使用它的派生类，同样的，这也是未知数量的代码。Protected数据成员同public数据成员一样也没有被封装，
// 因为在两种情况中，如果数据成员被修改，都有未知数量的客户代码会被破坏。这是违反直观的，但是一个经验丰富的库实现人员会告诉你，这就是真的。
// 一旦你将一个数据成员声明成public或者protected并且客户开始使用它，很难改变数据成员的任何东西。因为一旦修改了，太多的代码会被重新实现，重新测试，重新编辑文档，重新编译。从封装的角度来说，
// 真的只有两种访问级别：private（提供了封装）和其它的（没有提供封装）。

// 回到顶部
// 3. 总结
// 将数据成员声明为private。它为客户提供了对数据句法一致的访问，给予细粒度的访问控制，允许执行类的不变性，为类的作者提供实现的灵活性。
// Protected没有比public更具封装型。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Item 23 宁可使用非成员非友元函数函数也不使用成员函数

// 1. 非成员非友元好还是成员函数好？
// 想象一个表示web浏览器的类。这样一个类提供了清除下载缓存，清除URL访问历史，从系统中移除所有cookies等接口：

class WebBrowser
{
public:
    void clearCache(){};
    void clearHistory(){};
    void removeCookies(){};
    void clearEverything(){}; // calls clearCache, clearHistory, and removeCookies
};

// 当然，这个功能也可以通过非成员函数来提供，让它调用合适的成员函数就可以了：

void clearBrowser(WebBrowser &wb)
{
    wb.clearCache();
    wb.clearHistory();
    wb.removeCookies();
}

// 哪种方法才是更好的呢？是成员函数clearEverying还是非成员函数clearBrower?
// 2. 为什么非成员非友元函数好？
// 面向对象准则指出数据以及操作数据的函数应该被捆绑到一起，这就表明它建议成员函数是更好的选择。不幸的是，这个建议是不正确的。它曲解了面向对象的含义。
// 面向对象准则指出数据应该尽可能的被封装。违反直觉的是，成员函数 clearEverything 实际上并没有比非成员函数clearBrower有更好的封装性。并且提供非成员函数能够为web浏览器的相关功能提供更大的包装(packaging)灵活性，
// 相应的，就可以产生更少的编译依赖和更好的可扩展性。因此非成员函数比成员函数在许多方面都要好。明白为什么很重要。

// 2.1 用非成员非友元能产生更具封装性的类
// 以封装开始。如果一些东西被封装了，就意味着被隐藏起来了。封装的东西越多，就有更少的客户能看到它们。更少的客户能看到它们就意味着我们有更大的灵活性来进行对它们进行修改，因为我们的修改直接影响的是能看到这些修改的客户。
// 因此封装性越好，就赋予我们更大的能力来对其进行修改。这也是我们将封装摆在第一位的原因：它以一种只影响有限数量的客户的方式为我们修改东西提供了灵活性。

// 考虑同一个对象相关联的数据。看到这些数据的代码越少（也就是可访问它），数据就被封装的越好，我们就有更大的自由来修改这个对象的数据的一些特征，像数据成员的数量，类型等等。
// 通过确认有多少代码能够看到数据来判断数据的封装性是粗粒度的方法，我们可以计算出能够访问数据的函数的数量，能访问数据的函数越多，封装性越差。

//  Item 22解释了数据成员应该是private的，因为如果不是，未限定数量的成员函数就能够访问它们。这样就根本没有封装性可言。对于private的数据成员，能够访问它们的函数的数量为所在类的成员函数的数量加上友元函数的数量，
//  因为只有成员函数和友元函数能够访问private成员。考虑在一个成员函数（不仅能访问类的private数据，也能访问private函数,enums,typedef等等）和一个非成员非友元函数（私有的数据和函数都不能访问）之间做一个选择，
//  它们提供了相同的功能，能够产生更大封装性的选择是非成员非友元函数，因为他们没有增加能够访问类私有部分的函数的数量。
//  这就解释了为什么clearBrower（非成员非友元函数）要优于clearEverything:在WebBrowser类中，它产生了更大的封装。

// 在这点上有两件事情需要注意。第一，这个论述只适用于非成员非友元函数。友元同成员函数对类的私有成员有相同的访问权，因此对封装有相同的影响。
// 从封装的观;点来看，不是在成员和非成员函数之间进行选择，而是在成员和非成员非友元函数之间进行选择。（封装当然不是仅有的选择视角，Item 24中解释了在隐式类型转换中，需要在成员和非成员函数之间做出选择。）

// 第二件需要注意的事情恰恰是因为封装性指明类的函数为非成员函数这个观点，这并不意味着这个函数不能是别的类的成员函数。
// 我们可以将clearBrower声明成一个utility类的静态成员函数。只要它不是WebBrowser的一部分（或者一个友元），它就不会影响WebBrower的private成员的封装性。

// 2.2 用非成员非友元可以减少编译依赖
// 在c++中，一个更加自然的方法是使clearBrower成为同WebBrowser有相同命名空间的非成员函数：

namespace WebBrowserStuff
{

    class WebBrowser_1
    {
    };

    void clearBrowser(WebBrowser_1 &wb){};
} // namespace WebBrowserStuff

// 不仅仅是更加自然，因为命名空间不像类，它是可以跨文件的。这是很重要的，因为像clearBrower这样的函数是很便利的函数。既不是成员也不是友元，对WebBrower类没有特殊访问权，
// 因此它不能提供WebBrowser客户没有获取到的其他任何功能。举个例子，如果clearBrower这个函数不存在，客户只好自己调用clearCache,clearHistory,和removeCookies。

// 像webBrower这样的类可以有大量的便利函数，一些和标签相关，另一些和打印相关还有一些和cookie管理相关等等。通常大多数客户只对其中的一部分有兴趣。没有理由让只对标签便利函数感兴趣的客户编译依赖于cookie相关的便利函数。
// 将它们分开的直接的方法是将它们声明在不同的头文件中。

// 注意标准C++库就是这么组织的。它并没有在std命名空间中将所有东西包含在一个单一的<C++ Stand Library>头文件中，而是有许多头文件（<vector>,<algorithm>,<memory>等等）,
// 每个头文件声明了std命名空间中的一部分功能。只使用vector相关功能客户不需要#include <memory>;不需要使用list的客户不必#include <list>。这就允许客户只编译依赖于它们实际用到的部分。
// （Item 31中讨论了减少编译依赖的其他方法）。当一个功能来源于一个类的成员函数，那么将其分割就是不可能的，因为一个类必须被定义在一个整体中。它不能再分了。

// 2.2 用非成员非友元可以更好的提供扩展性
//  将所有的便利函数放在不同的头文件中——但放在一个命名空间中——同样意味着客户可以很容易的对便利函数进行扩展。他们需要做的是向命名空间中添加更多的非成员非友元函数。
//  举个例子，如果一个WebBrower客户决定实现图片下载相关的便利函数，他只需要创建一个头文件，在命名空间WebBrowserStuff中将这些函数进行声明。新函数能像旧的函数一样同它们整合在一起。
//  这也是类不能提供的另外一个性质，因为客户是不能对类定义进行扩展的。当然，客户可以派生出新类，但派生类没有权限访问基类的封装成员(像private成员)，这样的“扩展功能”就是二等身份。此外，正如Item 7中解释的，并不是所有类都被设计成基类。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Item 24 如果函数的所有参数都需要类型转换，将其声明成非成员函数

// 1. 将需要隐式类型转换的函数声明为成员函数会出现问题
// 使类支持隐式转换是一个坏的想法。当然也有例外的情况，最常见的一个例子就是数值类型。举个例子，如果你设计一个表示有理数的类，允许从整型到有理数的隐式转换应该是合理的。
// 在C++内建类型中，从int转换到double也是再合理不过的了（比从double转换到int更加合理）。看下面的例子：

// class Rational_3 {
// public:
// Rational_3(int numerator = 0,int denominator = 1);  // ctor is deliberately not explicit, // allows implicit int-to-Rational conversions

// int numerator() const; // accessors for numerator and

// int denominator() const; // denominator — see Item 22

// const Rational_3 operator*(const Rational_3& rhs) const;

// private:
// int numerator;
// int denominator;
// };

// 你想支持有理数的算术运算，比如加法，乘法等等，但是你不知道是通过成员函数还是非成员函数，或者非成员友元函数来实现。你的直觉会告诉你当你犹豫不决的时候，你应该使用面向对象的特性。
// 有理数的乘积和有理数类相关，所有将有理数的operator*实现放在Rationl类中看上去是很自然的事。但违反直觉的是，Item 23已经论证过了将函数放在类中的方法有时候会违背面向对象法则，
// 现在我们将其放到一边，研究一下将operator*实现为成员函数的做法：

// class Rational {
// public:
// const Rational operator*(const Rational_3& rhs) const;
// };

// （如果你不明白为什么函数声明成上面的样子——返回一个const value值，参数为const引用，参考Item 3,Item 20和Item21）

// 这个设计让你极为方便的执行有理数的乘法：

// Rational_3 oneEighth(1, 8);
// Rational_3 oneHalf(1, 2);
// Rational_3 result = oneHalf * oneEighth; // fine
// // result = result * oneEighth; // fine

// 但是你不满足。你希望可以支持混合模式的操作，例如可以支持int类型和Rational类型之间的乘法。这种不同类型之间的乘法也是很自然的事情。
// 当你尝试这种混合模式的运算的时候，你会发现只有一半的操作是对的：

// result = oneHalf * 2; // fine
// result = 2 * oneHalf; // error!
//  这就不太好了，乘法是支持交换律的。

// 2. 问题出在哪里？
// 将上面的例子用等价的函数形式写出来，你就会知道问题出在哪里：

// result = oneHalf.operator*(2); // fine2
// result = 2.operator*(oneHalf ); // error!

// oneHalf对象是Rational类的一个实例，而Rational支持operator*操作，所以编译器能调用这个函数。然而，整型2却没有关联的类，也就没有operator*成员函数。编译器同时会去寻找非成员operator*函数（也就是命名空间或者全局范围内的函数)
//  result = operator*(2, oneHalf ); // error!

// 但是在这个例子中，没有带int和Rational类型参数的非成员函数，所以搜索会失败。

// 再看一眼调用成功的那个函数。你会发现第二个参数是整型2，但是Rational::operator*使用Rational对象作为参数。这里发生了什么？为什么都是2，一个可以另一个却不行？

// 没错，这里发生了隐式类型转换。编译器知道函数需要Rational类型，但你传递了int类型的实参，它们也同样知道通过调用Rational的构造函数，可以将你提供的int实参转换成一个Rational类型实参，这就是编译器所做的。它们的做法就像下面这样调用：

// const Rational_3 temp(2); // create a temporary Rational object from
// auto result_1 = oneHalf * temp; // same as oneHalf.operator*(temp);

// 当然，编译器能这么做仅仅因为类提供了non-explicit构造函数。如果Rational类的构造函数是explicit的，下面的两个句子都会出错：

// result = oneHalf * 2; // error! (with explicit ctor);
// // can’t convert 2 to Rational

// result = 2 * oneHalf; // same error, same problem

// 这样就不能支持混合模式的运算了，但是至少两个句子的行为现在一致了。

// 然而你的目标是既能支持混合模式的运算又要满足一致性，也就是，你需要一个设计使得上面的两个句子都能通过编译。回到上面的例子，当Rational的构造函数是non-explicit的时候，为什么一个能编译通过另外一个不行呢？
// 看上去是这样的，只有参数列表中的参数才有资格进行隐式类型转换。而调用成员函数的隐式参数——this指针指向的那个——绝没有资格进行隐式类型转换。这就是为什么第一个调用成功而第二个调用失败的原因。
// 解决方法是什么？
// 然而你仍然希望支持混合模式的算术运行，但是方法现在可能比较明了了：使operator*成为一个非成员函数，这样就允许编译器在所有的参数上面执行隐式类型转换了：

// class Rational {

// ... // contains no operator*

// };

// const Rational operator*(const Rational& lhs, // now a non-member

// const Rational& rhs) // function

// {

// return Rational(lhs.numerator() * rhs.numerator(),

// lhs.denominator() * rhs.denominator());

// }

// Rational oneFourth(1, 4);

// Rational result;

// result = oneFourth * 2; // fine

// result = 2 * oneFourth; // hooray, it works!

// 4. Operator*应该被实现为友元函数么？
// 故事有了一个完美的结局，但是还有一个挥之不去的担心。Operator*应该被实现为Rational类的友元么？

// 在这种情况下，答案是No。因为operator*可以完全依靠Rational的public接口来实现。上面的代码就是一种实现方式。我们能得到一个很重要的结论：成员函数的反义词是非成员函数而不是友元函数。太多的c++程序员认为一个类中的函数如果不是一个成员函数（举个例子，需要为所有参数做类型转换），那么他就应该是一个友元函数。上面的例子表明这样的推理是有缺陷的。尽量避免使用友元函数，就像生活中的例子，朋友带来的麻烦可能比从它们身上得到的帮助要多。

// 5. 其他问题
// 如果你从面向对象C++转换到template C++，将Rational实现成一个类模版，会有新的问题需要考虑，并且有新的方法来解决它们。这些问题，方法和设计参考Item 46。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//  Item 25 实现一个不抛出异常的swap
//  2. swap的傻瓜实现方式及缺陷
// 2.1 swap函数的默认实现
// Swap函数就是将两个对象的值进行交换，可以通过使用标准的swap算法来实现：

template <typename T> // typical implementation of std::swap;
void swap(T &a, T &b) // swaps a’s and b’s values
{
    T temp(a);
    a = b;
    b = temp;
}
// 只要你的类型支持拷贝（拷贝构造函数和拷贝赋值运算符），默认的swap实现不需要你做一些特别的工作来支持它。

// 2.2 swap函数默认实现的缺陷——有可能效率低
// 然而，默认的swap实现也许并没有让你激动，它包括三次拷贝：a 拷贝到temp，b拷贝到a, temp拷贝到b。对于一些类型来说，这些拷贝不是必须的，默认的swap将你从快车道拉到了慢车道。
// 这些不需要拷贝的类型内部通常包含了指针，指针指向包含真实数据的其他类型。使用这种设计方法的一个普通的例子就是“pimpl idiom”(指向实现的指针 Item 31).举个例子：

class WidgetImpl
{ // class for Widget data;
public:
private:
    int a, b, c;           // possibly lots of data —
    std::vector<double> v; // expensive to copy!
};

class Widget
{ // class using the pimpl idiom
public:
    Widget(const Widget &rhs);
    Widget &operator=(const Widget &rhs) // to copy a Widget, copy its  WidgetImpl object. For details on implementing operator= in general see Items 10, Item 11, and Item 12.
    {
        *pImpl = *(rhs.pImpl);
    }

private:
    WidgetImpl *pImpl; // ptr to object with this

}; // Widget’s data

// 为了交换两个Widget对象的值，我们实际上唯一需要做的是交换两个pImpl指针，但是默认的swap算法没有办法能够获知这些。它不仅拷贝了三个Widget对象，还拷贝了三个WidgetImpl对象。非常没有效率，也不令人鸡冻。

// 3. 如何实现一个高效的swap
// 3.1 为普通类定义全特化版本swap
// 我们需要做的就是告诉std::swap当Widget对象被swap的时候，执行swap的方式是swap内部的pImpl指针。也就是为Widget定制一个std::swap。这是最基本的想法，看下面的代码，但是不能通过编译

// template<> // this is a specialized version
// void swap<Widget>(Widget& a, Widget& b)  // of std::swap for when T is  Widget
// {
// swap(a.pImpl, b.pImpl); // to swap Widgets, swap their  pImpl pointers; this won’t compile
// } //

// 开始的”templpate<>”说明这是对std::swap的模板全特化（total template specializaiton）,名字后面的”<Widget>”是说明这个特化只针对T为Widget类型。
// 换句话说，当泛化的swap模板被应用到Widget类型时，应该使用上面的实现方法。一般来说，我们不允许修改std命名空间的内容，但是却允许使用我们自己创建的类型对标准模板进行全特化。
// 但是这个函数不能编译通过。这是因为它尝试访问a和b中的pImpl指针，它们是private的。我们可以将我们的特化函数声明成friend，但是传统做法却是这样：在Widget中声明一个真正执行swap的public成员函数swap，让std::swap调用成员函数：

// class Widget { // same as above, except for the addition of the swap mem func

// public:
// void swap(Widget& other)
// {
// using std::swap; // the need for this declaration is explained later in this Item

// swap(pImpl, other.pImpl); // to swap Widgets, swap their pImpl pointers

// }

// };

// namespace std {

// template<> // revised specialization of

// void swap<Widget>(Widget& a, Widget& b) // std::swap
// {
// a.swap(b); // to swap Widgets, call their

// } // swap member function

// }

// 这种做法不仅编译能通过，同STL容器一致，它们都同时为swap提供了public成员函数版本和调用成员函数的std::swap版本。

///////////////////////////////////////////////////////////////////////////////////////////////

// Item 27 尽量少使用转型（casting）
// C++风格的cast
// C++同样提供四种新的casts形式（C++风格的casts:
//  const_cast<T>(expression)
//  dynamic_cast<T>(expression)
//  reinterpret_cast<T>(expression)
//  static_cast<T>(expression)

// 每种方法都有独特的用途：
// Const_cast是用来去除对象的常量性的（constness）。在四个C++风格的cast中，const_cast是唯一能做到这一点的。
// Dynamic_cast主要用来执行“安全的向下转型”，也就是决定一个特定类型的对象是否在一个继承体系中。这也是唯一一个不能用旧式风格语法来实现的cast。
// 它也是唯一一个可能会出现巨大的运行时开销的cast。（稍后会详细讲解）
// Reinterpret_cast被用来做低级的casts，结果可能依赖于编译器，也就是代码不能被移植，例如，将一个指针转换成int。这种casts除了用在低级代码中，其他地方很少见。本书中只出现过一次，就是在讨论如何为原生内存（raw memory）实现一个调试分配器(Item 50)。
// Static_cast能被用来做强制的显示类型转换（比如，non-const对象转换成const对象(Item 3)，int转换成double等等。）它同样能够用来对这些转换进行反转（比如，void*转换成具体类型指针，指向base的指针转换成指向派生类的指针），但是不能从const转换成非const对象（只有const_cast能这么做）。

// 2. 使用cast会产生运行时代码——不要认为你以为的就是你以为的
// 许多程序员认为cast除了告诉编译器需要将一个类型当作另外一个类型之外，没有做任何事情，但这个一个误区。任何种类的类型转换（不管显示cast还是隐式转换）都会产生运行时代码。举个例子：

int x, y;
double d = static_cast<double>(x) / y; // divide x by y, but use floating point division

// 将int x转换成double肯定会产生代码，因为在大多数系统架构中，int的底层表示同double是不一样的。这也许不会让你吃惊，但下面的例子可能亮瞎你的双眼：

class Base
{
};
class Derived : public Base
{
};
Derived d;
// Base *pb = &d;    // implicitly convert Derived* ⇒ Base*

// 这里我们只是创建了一个指向派生类对象的基类指针，但有时候，这两个指针（Derived*和Base*）值将会不一样。
// 在上面的情况中，运行时会在Derived*指针上应用一个偏移量来产生正确的Base*指针值。
// 最后这个例子表明一个对象（比如Derived类型的对象）可能有多于一个的地址（比如，当Base*指针指向这个对象和Derived*指向这个对象时有两个地址）。
// 这在C，java和C#中不可能发生。事实上，当使用多继承时，这种情况总会发生，但在单继承中也能发生。这意味着在C++中你应该避免对一些东西是如何布局的做出假设。例如，将对象地址转换成char*指针然后在此指针上面进行指针算术运算几乎总是会产生未定义行为。
// 但是注意我说过偏移量“有时候“是需要的。对象的布局方式和地址被计算的方式会随编译器的不同而不同。这意味着仅仅因为你了解一种平台上的布局和转型并不意味着在别的平台上也能如此工作。世界上充满了从中吸取教训的悲哀的程序员。

// 3. Cast很容易被误用——无效状态是如何产生的
// 关于cast的一件有趣的事情是容易写出看上去正确但实际错误的代码。比如，许多应用框架需要派生类中的虚函数实现首先要调用基类部分。
// 假设我们有一个Window基类和一个SpecialWindow派生类，两个类中都定义了onResize虚函数。进一步假设SpecialWindow的onResize函数首先要调用Window的onResize函数。
// 下面的实现方式看上去正确，实际上并非如此：

class Window
{ // base class
public:
    virtual void onResize() // base onResize impl
};

class SpecialWindow : public Window
{ // derived class
public:
    virtual void onResize()
    {                                          // derived onResize impl;
        static_cast<Window>(*this).onResize(); // cast *this to Window,then call its onResize; this doesn’t work! do SpecialWindow-// specific stuff
    }
};

// 我已经对代码中的cast标注了红色。（它是新风格的cast，使用旧风格的转换也不会改变如下事实）。正如你所期望的，
// 代码将*this转换成一个window对象。因此调用onResize时会触发Window:: onResize。你可能想不到的是它并没有在当前的对象上触发相应的函数。
// 相反，转型动作为*this的基类部分创建了一份新的临时拷贝，onResize是在这份拷贝上被触发的！
// 上面的代码没有在当前对象上调用Window::onResize然后在此对象上执行SpecialWindow的指定动作——它在执行特定动作之前，
// 在当前对象基类部分的拷贝之上调用了Window::onResize。如果Window::onResize修改了当前对象（很有可能，既然onResize是non-const成员函数），
// 当前的对象（Window对象）是不会被修改的。修改的是对象的拷贝。然而如果SpecialWIndow::onResize修改当前对象，当前对象将会被修改，
// 导致上面代码会为当前对象留下一个无效状态：基类部分没有被修改，派生类部分却被修改了。
// 解决方法是消除cast的使用，你不想欺骗编译器让其把*this当作一个基类对象。你想的是在当前对象上调用onResize的基类版本。所以按照下面的方法做：

class SpecialWindow_1 : public Window
{
public:
    virtual void onResize()
    {
        Window::onResize(); // call Window::onResize on *this
    }
};

// 4. Dynamic_cast 分析
// 4.1 Dynamic_cast速度很慢
// 在深入研究dynamic_cast的设计含义之前，我们能观察到dynamic_cast的很多实现其速度是非常慢的。举个例子，至少有一种普通的实现在某种程度上是基于类名称的字符串比较。
// 如果你正在一个4层深的单继承体系的对象上执行dynamic_cast，在这样一种实现（也就是上面说的普通实现）下每个dynamic_cast至多可能调用四次strcmp来比较类名称。
// 一个层次更深的继承或者一个多继承可能开销会更大。这样实现是有原因的（它们必须支持动态链接（dynamic linking））。
// 因此，除了要对使用cast时的一般问题保持机敏，在对性能敏感的代码中更要对dynamic_cast的使用保持机敏。

// 4.2 Dynamic_cast的两种替代方案
// 你需要dynamic_cast是因为你想在你坚信其是派生类对象之上执行派生类操作，但你只能通过基类指针或基类引用来操作此对象。有两种普通的方法避免使用dynamic_cast

// 第一，  使用容器直接存储派生类对象指针（通常情况下使用智能指针，见Item 13），这样就消除了通过基类接口来操纵这些对象的可能。
// 举个例子，在我们的window/SpecialWindow继承体系中，只有SpecialWindows支持blink，不要像下面这样做：

class Window
{
};
class SpecialWindow : public Window
{
public:
    void blink();
};

typedef std::vector<std::shared_ptr<Window>> VPW; // on tr1::shared_ptr
VPW winPtrs;

for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)
{ // undesirable code: uses dynamic_cast
    if (SpecialWindow *psw = dynamic_cast<SpecialWindow *>(iter->get()))
    {
        psw->blink();
    }
}

// 而是用下面的做法：
typedef std::vector<std::shared_ptr<SpecialWindow>> VPSW;
VPSW winPtrs;
// better code: uses no dynamic_cast
for (VPSW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)
{
    (*iter)->blink();
}

// 当然这种方法不允许你在同一个容器中存储所有可能的Window派生物。要达到这个目的，你可能需要多个类型安全的容器。
// 第二， 在基类中提供虚函数。举个例子，虽然只有SecialWindos支持blink，你同样可以在基类中声明一个blink,但默认实现是什么都不做：

class Window
{
public:
    virtual void blink() {} // default impl is no-op; see Item 34 for why a default impl may be a bad idea
};

class SpecialWindow : public Window
{
public:
    virtual void blink() {} // in this class, blink

    // does something
};

typedef std::vector<std::shared_ptr<Window>> VPW;
VPW winPtrs; // container holds (ptrs to) all possible Window types
for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter)
{ // note lack of dynamic_cast
    (*iter)->blink();
}

// 上面的两种方法不是在任何情况下都能使用，但是在许多情况下，它们为dynamic_cast提供了一种可行的替代方案。当他们确实能做到你想要的，你应该拥抱它们。
// 这个例子同样表明如果你发现你自己想使用cast了，它就标志着你可能会使用错误的方式来应用它。使用dynamic_cast的时候也是如此。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 28 不要返回指向对象内部数据(internals)的句柄（handles）

// 假设你正在操作一个Rectangle类。每个矩形可以通过左上角的点和右下角的点来表示。为了保证一个Rectangle对象尽可能小，你可能决定不把定义矩形范围的点存储在Rectangle类中，
// 而是把它放入一个辅助结构体中，Rectangle中声明一个指向它的指针就可以了：

class Point
{ // class for representing points
public:
    Point(int x, int y);
    void setX(int newVal);
    void setY(int newVal);
};

struct RectData
{               // Point data for a Rectangle
    Point ulhc; // ulhc = “ upper left-hand corner”
    Point lrhc; // lrhc = “ lower right-hand corner”
};

class Rectangle
{
private:
    std::shared_ptr<RectData> pData;
};

// 1. 由返回指向对象内部数据的引用所引发的两个问题
// 1.1 问题分析
// 因为Rectangle的客户需要能够获知一个矩形的范围，类因此提供了upperLeft和lowerRight函数。然而，Point是一个自定义的类型，
// 所以你需要留意Item 20：对于用户自定义类型，按引用传递比按值传递更高效，这些函数返回了对底层Point对象的引用：

class Rectangle
{
public:
    Point &upperLeft() const { return pData->ulhc; }
    Point &lowerRight() const { return pData->lrhc; }
};

// 这种设计可以编译通过，但却是错误的。事实上，它是自相矛盾的。一方面，upperLeft和lowerRight被声明成const成员函数，
// 因为它们只用来为客户提供获知矩阵包含哪些点的方法，并没有让客户修改矩阵（Item 3）。另一方面，两个函数都返回指向私有内部数据的引用——而此引用能够被调用者用来修改内部数据！举个例子：

// Point coord1(0, 0);

// Point coord2(100, 100);

// const Rectangle rec(coord1, coord2);

// // rec is a const rectangle from

// // (0, 0) to (100, 100)
// rec.upperLeft().setX(50); // now rec goes from
// // (50, 0) to (100, 100)!

// 注意upperLeft的调用者是如何利用返回的指向rec内部的Point数据成员的引用来修改这个成员的。但是rec是const变量。

// 我们能从中学到两点。首先，一个数据成员的封装性同以这个数据成员的引用作为返回值的可访问级别最高（most accessible）的成员函数一致。在这种情况下，虽然ulhc和lrhc对于Rectangle来说是private的，它们实际上是public的，因为public函数upperLeft和lowerRight返回了指向它们的引用。第二，如果一个const成员函数返回指向数据的引用，而此数据被存储在当前对象之外，那么函数的调用者就能够修改这些数据。（这是bitwise constness局限性的附带结果 Item 3）。

// 1.2 句柄(handles)不仅包含引用，也包含指针和迭代器
// 我们讨论的都是返回引用的成员函数，但是如果它们返回指针或者迭代器，同样原因导致的同样问题也将会存在。引用，指针和迭代器都是句柄（handles），返回一个指向对象内部数据的句柄常常有破坏封装型的风险。也会导致从const成员函数传递出去的对象的状态被修改掉。

// 1.3 内部数据(internals data)不仅包含数据成员，也包括成员函数
// 我们通常认为一个对象的“内部数据”只针对数据成员，但非public的成员函数也是对象内部数据的一部分。因此，禁止返回指向它们的句柄同样重要。这意味着绝不要从成员函数中返回一个指向更低访问级别的函数的指针。如果你这么做了，有效的访问级别就是访问级别更高的那个函数，因为客户可以获得访问级别更低的函数的指针，然后通过指针来调用此函数。

// 回到顶部
// 2. 解决上面两问题的方法，为引用添加const

// 返回指向成员函数指针的函数并不普通，让我们重新关注Rectangle类和它的upperLeft和lowerRight成员函数。我们发现的两个问题可以通过简单的为其返回值添加const来消除：

// class Rectangle {
// public:
// ...
// const Point& upperLeft() const { return pData->ulhc; }
// const Point& lowerRight() const { return pData->lrhc; }
// ...
// };

// 使用这个修改后的设计，客户可以读取定义一个矩形的点，但是不能修改它们。这意味着upperLeft和lowerRight的声明不再是一个谎言，因为它们不再允许调用者修改对象的状态。对于封装问题，我们的意图是让客户能够看到组成矩形的点，因此我们故意放松了封装型。更加重要的是，这是有局限性的“放松“：这些函数是只读的。

// 回到顶部
// 3. 返回const引用会引入新的问题
// 即便如此，upperLeft和lowerRight仍然返回了指向对象内部数据的句柄，这可能会在其它方面出现问题。特别是它能导致悬挂指针：指向部分对象的句柄不再存在。这种对象消失问题的最常见的根源在于按值返回的函数。举个例子，考虑一个为GUI对象返回边界框的函数，边界框用矩阵表示：

// class GUIObject { ... };

// const Rectangle                                      // returns a rectangle by

// boundingBox(const GUIObject& obj);     // value; see Item 3 for why

// // return type is const

// 现在考虑一个客户如果使用这个函数：

// 1 GUIObject *pgo; // make pgo point to
// 2 ... // some GUIObject
// 3 const Point *pUpperLeft = // get a ptr to the upper
// 4 &(boundingBox(*pgo).upperLeft()); // left point of its
// 5 // bounding box

// 这个对boundingBox的调用将返回一个新的临时Rectangle对象.这个对象没有名字，我们叫它temp。upperLeft将在temp上被调用，这个调用返回了指向temp内部数据的引用，temp内部数据就是组成矩形的一个Point。pUpperLeft将会指向这个Ponit对象。到现在为止一切正常，但是还没完呢，因为在声明结束时，boundingBox的返回值——temp——将会被销毁，这将间接导致temp的Point对象被析构。这样使得pUpperLeft指向一个不再存在的对象；pUpperLeft在创建它的语句结束时变成了悬挂指针！

// 这也是为什么任何返回指向对象内部数据的句柄的函数都是危险的。不论这个句柄是一个指针，或者一个函数，或者一个迭代器。也不管这个函数有没有被声明为const。也不管成员函数返回的句柄是否为const。问题的关键在于函数有没有返回句柄，因为一旦返回了，就会有句柄比其指向的对象存在时间更长的危险。

// 回到顶部
// 4. 例外的情况
// 这并不意味着你永远不应该让一个成员函数返回一个句柄。有时候你必须这么做。举个例子，operator[]允许你从string或者vector中将单个元素摘出来，这些operator[]返回的就是指向容器内部数据的引用（Item 3）——容器被销毁的时候，它里面的数据也被销毁。但这样的函数只是一个例外。

// 回到顶部
// 5. 总结
// 避免返回指向对象内部数据的句柄（引用，指针或者迭代器）。不返回句柄可以增强封装性，帮助const成员函数的行为为真正的const，减少悬挂指针被创建的可能。

/////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 32 确保public继承建立“is-a”模型
// 1. 何为public继承的”is-a”关系
// 在C++面向对象准则中最重要的准则是：public继承意味着“is-a”。记住这个准则。

// 如果你实现一个类D(derived) public 继承自类B(base),你在告诉c++编译器（也在告诉代码阅读者），每个类型D的对象也是一个类型B的对象，反过来说是不对的。你正在诉说B比D表示了一个更为一般的概念，
// 而D比B表现了一个更为特殊的概念。你在主张：任何可以使用类型B的地方，也能使用类型D，因为每个类型D的对象都是类型B的对象；反过来却不对，也就是可以使用类型D的地方却不可以使用类型B：D是B，B不是D。

// C++ 会为public继承强制执行这个解释。看下面的例子：
class Person
{
};
class Student : public Person
{
};
// 从日常生活中我们知道每个学生都是一个人，但并不是每个人都是学生。这正是上面的继承体系所主张的。我们期望对人来说为真的任何事情——例如一个人有出生年月——对学生来说也是真的。
// 我们不期望对学生来说为真的任何事情——例如在一个特定的学校登记入学——对普通大众来说也是真的。人的概念比学生要更加一般化；而学生是人的一个特定类型。

// 在C++的领域内，需要Person类型（或者指向Person的指针或者指向Person的引用）参数的任何函数也同样可以使用Student参数（或者指针或引用）

void eat(const Person &p){};    // anyone can eat
void study(const Student &s){}; // only students study
Person p;                       // p is a Person
Student s;                      // s is a Student
eat(p);                         // fine, p is a Person
eat(s);                         // fine, s is a Student, and a Student is-a Person
study(s);                       // fine
study(p);                       // error! p isn’t a Student
// 这仅对public继承来说是有效的。C++仅仅在Student公共继承自Person的时候，其行为表现才会如上面所描述的。Private继承的意义就完全变了（Item 39）,protected继承是至今都让我感到困惑的东西。

// 2. Public继承可能误导你——例子一，企鹅不会飞
// Public继承和”is-a”是等价的听起来简单，但有时候你的直觉会误导你。举个例子，企鹅是鸟这是个事实，鸟能飞也是事实。如果尝试用C++表示，将会产生下面的代码：

class Bird
{
public:
    virtual void fly(); // birds can fly
};

class Penguin : public Bird
{ // penguins are bird
};

// 我们突然陷入了麻烦，因为这个继承体系表明了企鹅会飞，我们知道这不是真的。发生了什么？
// 2.1 处理上述问题的方法一——更加精确的建模，不定义fly
// 在这种情况下，我们是一种不精确语言——英语——的受害者。当我们说鸟能飞，我们并没有说所有的鸟都能飞，通常情况下只有有这个能力的才行。
// 如果更加精确一些，我们能够识别出有一些不能飞的鸟的种类，就可以使用如下的继承体系，它更好的模拟了现实：

class Bird
{ // no fly function is declared
};

class FlyingBird : public Bird
{
public:
    virtual void fly();
};

class Penguin : public Bird
{ // no fly function is declared
};

// 这个继承体系比原来的设计更加忠于现实。
// 关于这些家禽的事情还没有完，因为对于一些软件系统来说，没有必要对能飞和不能飞的鸟进行区分。如果你的应用更加关注鸟嘴和鸟的翅膀而对会不会飞漠不关心，
// 最开始的两个类的继承体系就足够了。这反应了一个简单的事实：没有一个理想的设计适用于所有软件。最好的设计取决于需要系统去做什么，无论是现在还是将来。如果你的应用没有与飞相关的知识，并且永远也不会有，
// 对能不能飞不做区分或许是一个完美并且有效的设计决策。事实上，能够区分它们的设计或许更可取，因为你尝试为其建模的这种区分有一天可能会从世界上消失。

// 2.2 处理上述问题的方法二——产生运行时错误
// 有另外一个学派来处理我上面所描述的“所有的鸟能飞，企鹅是鸟，企鹅不能飞”问题。就是重新为企鹅定义fly函数，但是让其产生运行时错误：

void error(const std::string &msg); // defined elsewhere

class Penguin : public Bird
{
public:
    virtual void fly() { error("Attempt to make a penguin fly!"); }
};

// 上面所说的可能会和你想的不一样，能够辨别它们很重要。上面的代码并没有说，“企鹅不能飞。”而是说，“企鹅能飞，但是它们如果尝试这么做会是一个错误”。

// 2.3 区分二者的不同——编译期错误和运行时错误
// 你如何才能说出它们的不同？从错误被检测出来的时间点看，“企鹅不能飞“这个禁令能够被编译器强制执行，但是如果违反“企鹅尝试飞行是一个错误”这个规则只能够在运行时能够被检测出来。
// 为了表示“企鹅不能飞”这个限制，你要确保对Penguin对象来说没有这样的函数被定义：

class Bird
{ // no fly function is declared
};

class Penguin : public Bird
{ // no fly function is declared
};

// 如果你尝试让企鹅飞起来，编译器会谴责你的行为：

Penguin p;
p.fly(); // error!
//  这同产生运行时错误的方法有着很大的不同。如果你使用运行时报错的方法，编译器对p.fly的调用不会说一句话。
// Item 18解释了好的接口应该在编译期就能够阻止无效代码，所以比起只能在运行时才能侦测出来错误的设计，你应该更加喜欢在编译期就能拒绝企鹅飞翔的设计。

// 3. Public继承可能误导你——例子二，矩形和正方形
// 可能你会做出让步是因为你对鸟类学知识的匮乏，但是你能够依靠你对初步几何的精通，对吧？矩形和正方形会有多复杂呢？
// 现在回答这个简单的问题：正方形类应该public继承自长方形类么？

// 你会说“当然应该！每个人都知道正方形是一个矩形，反之却不成立。”再真不过了，至少是在学校里面。但是我认为我们已经不在学校里面了。
// 考虑下面的代码：
class Rectangle
{
public:
    virtual void setHeight(int newHeight);
    virtual void setWidth(int newWidth);
    virtual int height() const; // return current values
    virtual int width() const;
};

void makeBigger(Rectangle &r) // function to increase r’s area
{
    int oldHeight = r.height();
    r.setWidth(r.width() + 10);      // add 10 to r’s width
    assert(r.height() == oldHeight); // assert that r’s   // height is unchanged
}
// 很清楚，断言永远不会出错，makeBigger只会修改r的宽度。高度永远不会被修改。

// 现在考虑下面的代码，使用public继承，可以使正方形被当作矩形处理：
class Square : public Rectangle
{
};
Square s;
assert(s.width() == s.height()); // this must be true for all squares
makeBigger(s);                   // by inheritance, s is-a Rectangle,

// so we can increase its area
assert(s.width() == s.height()); // this must still be true for all squares

// 很清楚的是第二个断言永远不能失败。根据定义，一个正方形的宽度和高度应该一样。
// 但是现在我们有一个问题。我们怎么才能使下面的断言一致呢？
// 在调用makeBigger之前，s的高度和宽度是一样的；
// 在makeBigger里面，s的宽度被改变了，但是高度却没有；
// makeBigger返回之后，s的高度和宽度仍然相同。（注意s被按引用传递给makeBigger，所以makeBigger修改了s本身，而不是s的拷贝）

// 欢迎来到public继承的精彩世界，你在其它领域学习而来的直觉（包括数学）,使用起来可能和你想要的不一样。上面例子的基本的难点在于适用于矩形的东西（宽度独立于高度被修改）
// 却不适用于正方形（长宽必须相同）。但是public继承主张适用于基类对象的任何东西同样适用于派生类对象。对于长方形和正方形的情况（还有Item38中涉及到的sets和lists的例子），
// 这个主张不再适用，所以使用public继承来为其建模是不正确的。编译器可能会让你这么做，但是正如我们刚刚看到的，我们不能够确保代码的行为是正确的。这也是每个程序员必须要学到的：编码编译通过了不代表它能工作。

// 4. 使用public继承要有新的洞察力
// 这些年里使用面向对象设计的时候软件上的直觉会让你失败，不要烦躁。这些知识仍然有价值，现在你的设计兵工厂中又添加了可供替换的继承，你必须用新的洞察力来扩大你的直觉，指导你合适的使用继承。
// 当一些人向你展示长达几页的函数时，你会想起企鹅继承自鸟类或者正方形继承自长方形这些让你感觉有趣的事情。它可能是处理事情的正确方法，只是不是特别像。

// 5. 其它两种类关系
// “is-a”关系不是存在类之间的仅有的关系。另外两个普通的类之间的关系是“has-a”和“is-implemented-in-terms-of”。
// 这些关系在Item38和Item39中被介绍。C++设计出现错误并非不常见，因为其他重要的类关系有可能不正确的被建模为”is-a”，所以你应该确保能明白这些关系之间的区别，并且知道C++中如何最好的塑造它们。

// 6. 总结
// Public继承意味着“is-a”.应用于base类的每件东西必须也能应用于派生类，因为每个派生类对象是一个基类对象。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 33 避免隐藏继承而来的名字
// 1. 普通作用域中的隐藏
// 名字实际上和继承没有关系。有关系的是作用域。我们都知道像下面的代码：
int x; // global variable
void someFunc()
{
    double x;      // local variable
    std::cin >> x; // read a new value for local x
}

// 读入x的声明指向的是本地的x而不是全局的x，因为内部作用域的名字将外部作用域的变量隐藏掉了。我们将作用域的这种情况用以下方式进行可视化：
// 当编译器在someFunc的作用域中时，遇到了名字x,它们先在本地作用域中查询是否有以这个名字命名的一些东西。因为存在，它们就不再检查别的作用域了。在这种情况中，someFunc的x是double类型，而global的x是int类型，这没有关系。C++的名字隐藏规则会对其进行处理：隐藏名字。不管与名字对应的是相同还是不同的类型都没有关系。在这种情况中，被命名为x的double隐藏了命名为x的int。
// 编译器是如何在继承体系中寻找名字的
// 现在进入继承。我们知道当我们处在一个派生类成员函数内部时，并且引用了一些基类的东西（例如，一个成员函数，一个typedef或者一个数据成员），编译器能够找到我们所引用的东西，因为派生类继承了声明在基类中的这些东西。
// 实际的工作方式是派生类的作用域被镶嵌在基类作用域内部

// 3. 继承体系中的名字是如何被隐藏的
// 考虑前面的例子，这次我们除了要重载mf1和mf3之外，也在Derived中添加一个mf3版本。（正如Item36中解释的，Derived中mf3的声明——一个继承而来的非virtual函数——会让这个设计看起来很可疑，但是为了理解继承下的名字可见性，我们忽略这个问题。）

class Base
{
private:
    int x;

public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);
};

class Derived : public Base
{
public:
    virtual void mf1();
    void mf3();
    void mf4();
};

// 这段代码产生的行为会让每个首次碰到这种情况的C++程序员都感到吃惊。基于作用域的名字隐藏规则并没有变化，所以基类中的名字为mf1和mf3的所有函数被派生类中相同名字的函数隐藏掉了。
// 从名字搜寻的角度来看，Base::mf1和Base::mf3不再被Derived继承了！

Derived d;
int x;
d.mf1();  // fine, calls Derived::mf1
d.mf1(x); // error! Derived::mf1 hides Base::mf1
d.mf2();  // fine, calls Base::mf2
d.mf3();  // fine, calls Derived::mf3
d.mf3(x); // error! Derived::mf3 hides Base::mf3

// 正如你所看到的，对于有相同名字的基类和派生类中的函数有，即使参数类型不同，上面的隐藏规则也同样适用，并且它和函数的虚与非虚没有关系。
// 在这个条款开始也是同样的方式，函数someFunc中的double x隐藏了全局作用域的int x，在这里Derived中的函数mf3隐藏了基类中名字为mf3的函数，即使参数类型不一样。

// 4. 如何将隐藏行为覆盖掉
// 这种行为背后的基本原理在于当你在一个库或者应用框架中创建一个新的派生类时，它能够阻止你突然从遥远的基类中继承重载函数。不幸的是，你总是想继承重载函数。事实上，如果你正在使用public继承并且没有继承重载函数，
// 你就违反了基类和派生类之间的”is-a”关系，这是Item 32中介绍的public继承的基本原则。这就是基本情况，你总是想对继承来的名字的默认隐藏行为进行覆盖。
// 4.1 方法一 使用using 声明
// 使用using声明来达到这个目的：

class Derived : public Base
{
public:
    using Base::mf1;
    using Base::mf3;
    virtual void mf1();
    void mf3();
    void mf4();
}

// 现在的继承就按照你所期望的进行工作了：
Derived d;
int x;
d.mf1();  // still fine, still calls Derived::mf1
d.mf1(x); // now okay, calls Base::mf1
d.mf2();  // still fine, still calls Base::mf2
d.mf3();  // fine, calls Derived::mf3
d.mf3(x); // now okay, calls Base::mf3 (The int x is
// implicitly converted to a double so that
// the call to Base::mf3 is valid.)

// 这意味着如果你的类继承自包含重载函数的基类，你想对其中的一些函数进行重新定义或者覆盖，你需要为每个即将被隐藏掉的名字包含一个using声明，如果你不这样做，你想继承的一些名字就会被隐藏。

//  4.2 方法二 使用forwarding函数
// 有时候你并不想继承基类的所有函数，这是可能的。在public继承下，你应该永远拒绝这种行为，因为，它违反了基类和派生类之间public继承的”is-a”关系。
// （这也是为什么上面的using声明放在派生类的public部分：基类中的public名字在public继承的派生类中应该也是public的）。然而在private继承中(见Item 39)，
// 它也是有意义的。举个例子，假设Derived私有继承自基类Base,Derived类想继承基类函数mf1的唯一版本是不带参数的版本。
// Using声明在这里就不工作了，因为一个using声明会使得所有继承而来的函数的名字在派生类中是可见的。这里可以使用不同的技术，也就是简单的forwarding函数：

class Base
{
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    // as before
};

class Derived : private Base
{
public:
    virtual void mf1() // forwarding function; implicitly
    {
        Base::mf1();
    } // inline — see Item 30. (For info on calling a pure virtual function, see Item 34.)
};

Derived d;
int x;
d.mf1();  // fine, calls Derived::mf1
d.mf1(x); // error! Base::mf1() is hidden

// 另外一种使用inline forwarding函数的地方是在使用比较老的编译器的时候，它们不支持通过using 声明来将继承而来的名字导入到派生类作用域中。
// 这就是关于继承和名字隐藏的全部故事，但是当继承同模板结合起来的时候，一个完全不同的“继承而来的名字被隐藏”问题就会出现，详情见 Item 43。

// 总结 派生类中的名字会将基类中的名字隐藏掉。在公共继承中，这绝不是令人满意的。 为了让隐藏起来的名字重见天日，使用using声明或者forwarding函数来达到目的。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 34 区分接口继承和实现继承
// 看上去最为简单的(public)继承的概念由两个单独部分组成：函数接口的继承和函数模板继承。这两种继承之间的区别同本书介绍部分讨论的函数声明和函数定义之间的区别完全对应。
// 1. 类函数的三种实现
// 作为一个类设计者，有时候你只想派生类继承成员函数的接口（声明）。有时候你想让派生类同时继承接口和实现，但是你允许它们覆盖掉继承而来的函数实现。但有时候你却想让派生类继承一个函数的接口和实现并且不允许它们被覆盖掉。

// 为了对这些不同的选择有一个更好的理解，考虑表示几何图形的类继承体系：

class Shape
{
public:
    virtual void draw() const = 0;
    virtual void error(const std::string &msg);
    int objectID() const;
};

class Rectangle : public Shape
{
};
class Ellipse : public Shape
{
};

// Shape是一个抽象类；是由纯虚函数draw所标记的。因此客户不能创建Shape类的实例，而只有它的派生类才可以。尽管如此，Shape对（public）继承自它的所有类会产生很大的影响，因为：
// 成员函数接口总是被继承，正如Item 32中解释的，public继承意味着“is-a”，也就是对基类来说为真的任何东西对派生类来说也必须为真。因此，如果一个函数可以被应用在一个类中，它也必须能被应用到它的派生类中。
// Shape类中声明了三个函数。第一个，draw,画出当前对象；第二个，error，当error需要被记录的时候被调用。第三个，objectID,为当前对象返回一个唯一的整型标识符。
// 每个函数以一种不同的方式被声明：draw是纯虚函数；error是简单的（不是纯的）虚函数；objectID是非虚函数。这些不同声明隐藏的含义是什么呢？

//  1.1 纯虚函数
// 考虑第一个纯虚函数draw:
class Shape
{
public:
    virtual void draw() const = 0;
};

// 纯虚函数的两个最具特色的特征是：它们必须被继承它们的任何具现类重新声明；在抽象类中它们通常情况下没有定义。将这两个特征放在一起，你就会发现：
// 声明纯虚函数的意图是让派生类只继承函数接口
// 这使得Shape::draw函数是非常有意义的，因为对于所有的Shape对象来说能够被画出来是一个合理的需求，但是Shape类不能为这个函数提供合理的默认实现，比如，画一个椭圆的算法和画一个矩形的算法是不一样的。
// Shape::draw的声明对派生具现类的设计者说，“你必须提供一个draw函数，但是我并不知道你该如何实现它。”
// 顺便说一下，为一个纯虚函数提供一个定义也是可能的。也就是你可以为Shape::draw提供一个实现，C++不会发出抱怨，但是调用它的唯一方式是在函数名前加上类名限定符：
Shape *ps = new Shape;      // error! Shape is abstract
Shape *ps1 = new Rectangle; // fine
ps1->draw();                // calls Rectangle::draw
Shape *ps2 = new Ellipse;   // fine
ps2->draw();                // calls Ellipse::draw
ps1->Shape::draw();         // calls Shape::draw
ps2->Shape::draw();         // calls Shape::draw

// 除了帮助你在鸡尾酒会上给你的程序员伙伴留下深刻印象之外，这个特性通常来说效用有限。然而，你在下面会看到，它可以作为一种机制为简单的（非纯的）虚函数提供比平常更加安全的默认实现。
// 1.2 非纯的虚函数
// 简单虚函数背后的故事同纯虚函数有些不太一样。通常情况下来说，派生类继承函数接口，但是简单虚函数提供了可能会被派生类覆盖的实现。如果你再想想，你会意识到：
// 声明一个简单虚函数的目的是让派生类继承一个函数接口或者一个默认实现。
// 考虑Shape::error的情况：

class Shape
{
public:
    virtual void error(const std::string &msg);
};
// 这个接口表明在遇到错误的时候每个类必须提供一个错误函数，但是每个类对错误如何进行处理可以自由控制。如果一个类不想做任何特殊的事情，那么调用基类Shape中error的默认实现就可以了。
// 也就是Shape::error的声明对派生类的设计者说，“你可以支持error函数，但如果你不想自己实现，你可以使用Shape类中的默认版本。”

// 1.2.1 同时为简单虚函数提供函数接口和默认实现是危险的
// 同时为简单虚函数提供函数接口和默认实现是危险的。为什么？考虑为XYZ航空公司设计了飞机继承体系。XYZ只有两种类型的的飞机，型号A和型号B，同种飞机的飞行方式相同。因此，XYZ设计了如下的继承体系：

class Airport
{
}; // represents airports

class Airplane
{
public:
    virtual void fly(const Airport &destination);
};

void Airplane::fly(const Airport &destination)
{
    // default code for flying an airplane to the given destination
}

class ModelA : public Airplane
{
};
class ModelB : public Airplane
{
};

// 为了表示所有的飞机必须支持fly函数，还有不同型号的飞机可能需要fly的不同实现，因此Airplane::fly被声明为virtual。然而，为了防止在ModelA和ModelB中实现同一份代码，我们为Airplane::fly提供了默认实现，ModelA和ModelB可以同时继承。
// 这是典型的面向对象设计。两个类分享同一个特征（实现fly的方式），所以一般的特征都会移到基类中，然后被派生类继承。这种设计使得类的普通特性比较清晰，防止代码重复，可以促进将来的增强实现，
// 使长期维护更加容易——这是面向对象如此受欢迎的原因，XYZ应该为此感到骄傲。

// 现在假设XYZ公司界定引入新类型的飞机，Model C。型号C和型号A和B不一样，它的飞行方式变了。
// XYZ的程序员为Model C在继承体系中添加了新类，但是他们如此匆忙的添加新类，以至于忘了重新定义fly函数：

class ModelC : public Airplane
{
    // no fly function is declared
};

// 在他们的代码中有类似下面的实现：
Airport PDX(); // PDX is the airport near my home
Airplane *pa = new ModelC;
pa->fly(PDX); // calls Airplane::fly!

// 这会是一个灾难：型号C的飞机尝试用型号A或者型号B的飞行方式去飞行。这不是增加旅客信心的行为。

// 1.2.2 解决方法一，将默认实现分离成单独函数
// 这里的问题不在于Airplane::fly有默认的行为，而在于允许 Model C在没有明确说明它需要基类行为的情况下继承了基类的行为。幸运的是，很容易为派生类提供只有在它们需要的情况下才为其提供的默认行为。
// 这个窍门断绝了虚函数接口和默认实现之间的联系。下面是实现的方法：

class Airplane
{
public:
    virtual void fly(const Airport &destination) = 0;

protected:
    void defaultFly(const Airport &destination);
};

void Airplane::defaultFly(const Airport &destination)
{
    // default code for flying an airplane to the given destination
}

// 这个机制也不是十分安全的（程序员仍然能够复制粘贴而导致错误），但是它比原来的设计可靠多了。因为对于Airplane::defaultFly来说，它是protected的是因为它是Airplane和它的派生类中的实现细节。
// 使用airplane的客户只关心它们能够起飞，而不管飞行是如何实现的。

// Airplane::defaultFly是一个非虚函数同样重要。因为没有派生类可以重定义这个函数，这也是Item36所描述的真理。如果defaultFly是虚的，就会有一个循环问题：当派生类想重新定义defaultFly但是忘了会怎样？

// 1.2.3 解决方法二，利用纯虚函数提供默认实现
// 一些人反对将函数接口和默认实现分离的想法，就像上面的fly和defaultFly一样。首先，它们意识到，繁殖出十分相关的函数名字污染了类命名空间。但是它们仍然同意将函数接口和默认实现分离。
// 它们如何处理这种看上去矛盾的事情呢？通过利用纯虚函数必须在具现派生类中重新声明这个事实，但是它们也有可能有自己的实现。下面的例子展示了Airplane继承体系是如何利用定义纯虚函数的能力的：

class Airplane
{
public:
    virtual void fly(const Airport &destination) = 0;
};

void Airplane::fly(const Airport &destination) // an implementation of a pure virtual function
{
    // default code for flying an airplane to the given destination
}

class ModelA : public Airplane
{
public:
    virtual void fly(const Airport &destination)
    {
        Airplane::fly(destination);
    }
};

class ModelB : public Airplane
{
public:
    virtual void fly(const Airport &destination)
    {
        Airplane::fly(destination);
    }
};

class ModelC : public Airplane
{
public:
    virtual void fly(const Airport &destination);
};
void ModelC::fly(const Airport &destination)
{
    // code for flying a ModelC airplane to the given destination
}

// 这个设计同前面的设计是基本相同的，除了纯虚函数体Airplane::fly代替了独立函数Airplane::defaultFly。从本质上来说，fly已经被分成了两个基本的组件。它的声明指定了接口（派生类必须使用它），
// 同时它的定义指定了默认行为（派生类可能会使用，但是只有在显示的请求的时候才会使用）
// 将fly和defaultFly合并到一起，你就会失去为两个函数提供不同保护级别的能力：过去是protected的代码（在defaultFly中）现在变成了public的（因为它在fly中)

// 1.3 非虚函数,最后，让我们看一看Shape的非虚函数，objectID :

class Shape
{
public:
    int objectID() const;
};

// 当一个成员函数是非虚的，就不想其在派生类中有不同的行为。事实上，一个非虚成员函数指定了一种超越特化的不变性（invariant over specialization），无论一个派生类被如何特化, 它的行为不可改变。
// 声明一个非虚函数的意图在于让派生类继承一个函数接口，并且有一个强制的实现，你可以将Shape::objectID的声明想象成如下，“每一个Shape对象都有一个函数来产生一个对象标识符，这个对象标识符以相同的方式计算出来。
// 计算方式由Shape::objectID的定义来决定，任何派生类都不应该尝试去修改它的定义”。因为一个非虚函数确定了一个超越特化的不变性，它永远不会在派生类中被定义，这一点将在Item36中进行讨论。
// 类设计者容易犯的两种错误:
// 对纯虚函数，简单虚函数和非虚函数进行声明的不同点在于允许你精确的指定派生类会继承什么: 只继承接口，继承接口和默认实现或者接口和强制实现。因为从根本上来说这些不同的声明类型意味着不同的东西，
// 在你声明成员函数的时候你必须在他们之间进行选择。如果你这么做了，你就应该能够避免没有经验的类设计者才会犯的两种普通错误。
// 2.1 错误一，将所有函数声明为非虚,第一种错误是将所有函数声明成非虚。这没有给派生类的特化留下任何余地,特别对于非虚析构函数来说是有问题的（Item 7）。当然，我们有足够的理由设计一个不被当作基类的类，在这种情况下，只声明非虚函数是合适的。
// 然而通常情况下，这些类在下面两种情况下被创建出来：要么是忽略了虚函数和非虚函数的区别，要么就是过度担心虚函数所花费的开销。事实是基本上任何被用作基类的类都会使用虚函数。（Item 7）
// 如果你关心虚函数的开销，允许我拿出80 - 20法则（Item 30也提到了），它表明了在一个典型的程序中，20 % 的代码会花费80 % 的运行时间。这个法则很重要，因为它意味着，平均来说，你的程序中的80 % 的函数调用可以是虚函数调用，
// 但对你的程序的性能影响却是很轻微的。在你对能否负担的起虚函数的开销进行担心之前，确保你所关注的代码是对程序有重大影响的20 % 的那一部分
// 2.1 错误二，将所有函数声明为虚函数 另外一个普通的问题是将所有成员函数声明成虚函数。有时候这么做是对的——Item 31中的接口类就是这么做的。然而，这也是一个类设计者缺乏坚定立场的标志。
// 一些函数不应该在派生类中被重定义，当碰到这种情况，你就应该把这个函数定义为非虚。不是说只要花费一点时间对函数进行重定义，就能使使类满足所有人的需求。如果你需要特化上的不变性，不要害怕说不

// 3. 总结 接口继承不同于实现继承。在public继承下，派生类总是会继承基类接口。 纯虚函数只是指定了接口继承。 简单虚函数指定了接口继承外加一个默认实现。 非虚函数指定了一个接口继承外加一个强制实现。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 35 考虑虚函数的替代者
// 1. 突破思维——不要将思维限定在面向对象方法上
// 你正在制作一个视频游戏，你正在为游戏中的人物设计一个类继承体系。你的游戏处在农耕时代，人类很容易受伤或者说健康度降低。因此你决定为其提供一个成员函数，healthValue，返回一个整型值来表明一个人物的健康度。
// 因为不同的人物会用不同的方式来计算健康度，将healthValue声明为虚函数看上去是一个比较明显的设计方式：

class GameCharacter
{
public:
    virtual int healthValue() const; // return character’s health rating;derived classes may redefine this
};

// healthValue没有被声明为纯虚函数的事实表明了会有一个默认的算法来计算健康度（Item 34）
// 这的确是设计这个类的一个明显的方式，在某种意义上来说，这也是它的弱点。因为这个设计是如此明显，你可能不会对其他的设计方法有足够的考虑。为了让你逃离面向对象设计之路的车辙，让我们考虑一些处理这个问题的其它方法

// 2. 替换虚函数的四种设计方法
// 2.1 通过使用非虚接口(non-virtual interface(NVI))的模板方法模式
// 一个很有意思的学派认为虚函数几乎应该总是private的。这个学派的信徒建议一个更好的设计方法是仍然将healthValue声明成public成员函数但是使其变为非虚函数，然后让它调用一个做实际工作的private虚函数，也就是,doHealthValue:

class GameCharacter
{
public:
    int healthValue() const // derived classes do not redefine // this — see Item 36  // do “before” stuff — see below
    {
        int retVal = doHealthValue(); // do the real work do “after” stuff — see below
        return retVal;
    }

private:
    virtual int doHealthValue() const // derived classes may redefine this default algorithm for calculating character’s health
    {
    }
};

// 在上面的代码中（这个条款中剩余的代码也如此），我在类定义中展示了成员函数体。正如Item30中所解释的，将其隐式的声明为inline。我使用这种方式的目的只是使你更加容易的看到接下来会发生什么。
// 我所描述的设计和inline之间是独立的，所以不要认为在类内部定义成员函数是有特定意义的，不是如此。、
// 客户通过public非虚成员函数调用private虚函数的基本设计方法被称作非虚接口（non-virtual interface(NVI)）用法。它是更一般的设计模式——模板方法模式（这个设计模式和C++模板没有任何关系）的一个特定表现。
// 我把非虚函数（healthValue）叫做虚函数的一个包装。

// NVI用法的一个优点可以从代码注释中看出来，也就是“do before stuff”和“do after stuff”。这些注释指出了在做真正工作的虚函数之前或之后保证要被执行的代码。这意味着这个包装函数在一个虚函数被调用之前，确保了合适的上下文的创建，
// 在这个函数调用结束后，确保了上下文被清除。举个例子，“before”工作可以包括lock a mutex，记录log，验证类变量或者检查函数先验条件是否满足要求，等等。”after”工作可能包含unlocking a mutex，验证函数的后验条件是否满足要求，
// 重新验证类变量等等。如果你让客户直接调用虚函数，那么没有什么好的方法来做到这些。

// 你可能意识到NVI用法涉及到在派生类中重新定义private虚函数——重新定义它们不能调用的函数！这在设计上并不矛盾。重新定义一个虚函数指定如何做某事，而调用一个虚函数指定何时做某事。
// 这些概念是相互独立的。NVI用法允许派生类重新定义一个虚函数，这使他们可以对如何实现一个功能进行控制，但是基类保有何时调用这个函数的权利。初次看起来很奇怪，但是C++中的派生类可以重新定义继承而来的private虚函数的规则是非常明智的。

// 对于NVI用法，虚函数并没有严格限定必须为private的。在一些类的继承体系中，一个虚函数的派生类实现需要能够触发基类中对应的部分，如果使得这种调用是合法的，虚函数就必须为protected，而不是private的。
// 有时一个虚函数甚至必须是public的（例如，多态基类中的析构函数——Item7）,但是这种情况下，NVI用法就不能够被使用了。

// 2.2 通过函数指针实现的策略模式
// NVI用法是public虚函数的一个很有意思的替换者，但是从设计的角度来说，有一点弄虚作假的嫌疑。毕竟，我们仍然使用了虚函数计算每个人物的健康度。
// 一个更加引人注目的设计方法是将计算一个人物的健康度同这个人物的类型独立开来——这种计算不必作为这个人物的一部分。举个例子，我们可以使用每个人物的构造函数来为健康计算函数传递一个函数指针，然后在函数指针所指的函数中进行实际的运算：

// class GameCharacter;    // forward declaration
// // function for the default health calculation algorithm

// int defaultHealthCalc(const GameCharacter& gc);

// class GameCharacter {
// public:
// typedef int (*HealthCalcFunc)(const GameCharacter&);
// explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc): healthFunc(hcf ){}

// int healthValue() const
// { return healthFunc(*this); }

// private:
// HealthCalcFunc healthFunc;
// };

// // 这个方法是另外一种普通设计模式的简单应用，也就是策略模式。同在GameCharacter继承体系中基于虚函数的方法进行对比，它能提供了一些有意思的灵活性：
// // 相同人物类型的不同实例能够拥有不同的健康度计算函数。举个例子：

// class EvilBadGuy: public GameCharacter {

// public:

// explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)

// : GameCharacter(hcf )

// { ... }

// ...

// };

// int loseHealthQuickly(const GameCharacter&); // health calculation
// int loseHealthSlowly(const GameCharacter&); // funcs with different
// // behavior
// EvilBadGuy ebg1(loseHealthQuickly); // same-type charac
// EvilBadGuy ebg2(loseHealthSlowly); // ters with different
// // health-related
// // behavior

// 它可以对当前的健康度计算函数进行替换。
// 此外，健康度计算函数不再是GameCharacter继承体系中的成员函数的事实意味着它不能对正在计算健康度的对象的内部数据进行特殊访问。例如，defaultHealthCalc对EvilBadGuy的非public部分没有访问权。如果一个人物的健康度计算仅仅依赖于人物的public接口，这并没有问题，但是如果精确的健康计算需要非public信息，在任何时候当你用类外的非成员非友元函数或者另外一个类的非友元函数来替换类内部的某个功能时，这都会是一个潜在的问题。这个问题在此条款接下来的部分会一直存在，因为我们将要考虑的所有其他的设计方法都涉及到对GameCharacter继承体系外部函数的使用。

// 作为通用的方法，非成员函数能够对类的非public部分进行访问的唯一方法就是降低类的封装性。例如，类可以将非成员函数声明为友元函数，或者对隐藏起来的部分提供public访问函数。使用函数指针来替换虚函数的优点是否抵消了可能造成的GameCharacter的封装性的降低是你在每个设计中要需要确定的。

// 2.3 通过tr1::function实现的策略模式

// 一旦你适应了模板以及它们所使用的隐式（implicit）接口(Item 41)，基于函数指针的方法看起来就非常死板了。为什么健康计算器必须是一个函数而不能用行为同函数类似的一些东西来代替（例如，一个函数对象）？如果它必须是一个函数，为什么不能是一个成员函数？为什么必须返回一个int类型而不是能够转换成Int的任意类型呢？

// 如果我们使用tr1::funciton对象来替换函数指针的使用，这些限制就会消失。正如Item54所解释的，这些对象可以持有任何可调用实体（也就是函数指针，函数对象，或者成员函数指针），只要它们的签名同客户所需要的相互兼容。这是我们刚刚看到的设计，这次我们使用tr1::function:

// class GameCharacter; // as before
// int defaultHealthCalc(const GameCharacter& gc); // as before
// class GameCharacter {
// public:
// // HealthCalcFunc is any callable entity that can be called with
// // anything compatible with a GameCharacter and that returns anything
// // compatible with an int; see below for details
// typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc;

// explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
// : healthFunc(hcf )
// {}
// int healthValue() const
// { return healthFunc(*this); }
// ...
// private:
// HealthCalcFunc healthFunc;
// };

// 正如你所看到的，HealthCalcFunc是对一个实例化tr1::function的typedef。这意味着它的行为像一个泛化函数指针类型。看看HealthCalcFunc对什么进行了typedef：

// 1 std::tr1::function<int (const GameCharacter&)>

// 这里我对这个tr1::function实例的“目标签名”（target signature）做了字体加亮。这个目标签名是“函数带了一个const GameCharacter&参数，并且返回一个int类型”。这个tr1::function类型的对象可以持有任何同这个目标签名相兼容的可调用实体。相兼容的意思意味着实体的参数要么是const GameCharacter&，要么可以转换成这个类型，实体的返回值要么是int，要么可以隐式转换成int。

// 同上一个设计相比我们看到（GameCharacter持有一个函数指针），这个设计基本上是相同的。唯一的不同是GameCharacter现在持有一个tr1::function对象——一个指向函数的泛化指针。这个改动是小的，但是结果是客户现在在指定健康计算函数上有了更大的灵活性：

// short calcHealth(const GameCharacter&);     // health calculation
// // function; note
// // non-int return type

// struct HealthCalculator {                     // class for health

// int operator()(const GameCharacter&) const // calculation function
// { ... } // objects
// };
// class GameLevel {
// public:

// float health(const GameCharacter&) const;    // health calculation

// ...                                                                 // mem function; note

// };                                                                 // non-int return type

// class EvilBadGuy: public GameCharacter { // as before

// ...

// };

// class EyeCandyCharacter: public GameCharacter { // another character
// ... // type; assume same

// };                                                               // constructor as
// // EvilBadGuy

// EvilBadGuy ebg1(calcHealth);                   // character using a
// // health calculation
// // function

// EyeCandyCharacter ecc1(HealthCalculator());          // character using a
// // health calculation
// // function object

// GameLevel currentLevel;
// ...
// EvilBadGuy ebg2(                                     // character using a

// std::tr1::bind(&GameLevel::health, // health calculation

// currentLevel,               // member function;

// _1)                               // see below for details

// );

// 你会因为tr1::function的使用而感到吃惊。它一直让我很兴奋。如果你不感到兴奋，可能是因为刚开始接触ebg2的定义，并且想知道对tr1::bind的调用会发生什么。看下面的解释：

// 我想说为了计算ebg2的健康度，应该使用GameLevel类中的健康成员函数。现在，GameLevel::health是一个带有一个参数的函数（指向GameCharacter的引用），但是它实际上有两个参数，因为它同时还有一个隐含的GameLevel参数——由this所指向的。然而GameCharacters的健康计算函数却只有一个参数：也就是需要计算健康度的GameCharacter。如果我们对ebg2的健康计算使用GameLevel::health，我们必须做一些“适配”工作，以达到只带一个参数（GameCharacter）而不是两个参数(GameCharacter和GameLevel)的目的。在这个例子中，我们想使用GameLevel对象currentLevel来为ebg2计算健康度，所以我们每次使用”bind”到currentLevel的GameLevel::health函数来计算ebg2的健康度。这也是调用tr1::bind所能做到的：它指定了ebg2的健康计算函数应该总是使用currentLevel作为GameLevel对象。

// 我跳过了tr1::bind调用的很多细节，因为这样的细节不会有很多启发意义，并且会分散我要强调的基本观点：通过使用tr1::function而不是一个函数指针，当计算一个人物的健康度时我们可以允许客户使用任何兼容的可调用实体。这是不是很酷。

// 2.4 “典型的”策略模式
//  如果你对设计模式比上面的C++之酷更有兴趣，策略模式的一个更加方便的方法是将健康计算函数声明为一个独立健康计算继承体系中的虚成员函数。最后的继承体系设计会是下面的样子：

// 如果你对UML符号不熟悉，上面的UML图说明的意思是GameCharacter是继承体系中的root类，EvilBadGuy和EyeCandyCharacter是派生类；HealthCalcFunc是root类，SlowHealthLoser和FastHealthLoser是派生类；每个GameCharacter类型都包含了一个指向HealthCalcFunc继承体系对象的指针。

// 下面是代码的骨架：

// class GameCharacter;                                                                      // forward declaration

// class HealthCalcFunc {

// public:

// ...

// virtual int calc(const GameCharacter& gc) const

// { ... }

// ...

// };

// HealthCalcFunc defaultHealthCalc;

// class GameCharacter {

// public:

// explicit GameCharacter(HealthCalcFunc *phcf = &defaultHealthCalc)

// : pHealthCalc(phcf)

// {}

// int healthValue() const

// { return pHealthCalc->calc(*this); }

// ...

// private:

// HealthCalcFunc *pHealthCalc;

// };

// 很容易识别出来这是人们所熟知的”标准”策略模式的实现，它也为现存的健康计算算法的调整提供了可能性，你只需要添加一个HealthCalcFunc的派生类就可以了。

// 2.5 替换方法总结

// 这个条款的基本建议是当为你所要解决的问题寻找一个设计方法时，考虑一下虚函数设计的替代方法。下面是我们介绍的设计方法回顾：

// 使用非虚接口用法(NVI idiom)，这是模板方法设计模式（Template Method design pattern），它用public非虚成员函数来包裹更低访问权的虚函数来实现。
// 用函数指针成员函数来替代虚函数，这是策略设计模式的分解表现形式。
// 用tr1::function数据成员来代替虚函数，它可以使用同目标签名（signature）相兼容的任何可调用实体。这也是策略设计模式的一种形式。
// 将一个继承体系中的虚函数替换为另外一个继承体系的虚函数。这是策略设计模式的传统实现方法。
// 这并不是替换虚函数的所有设计方法，但是应该足够使你确信这些方法是确实存在的。进一步来说，它们的优缺点使你更加清楚你应该考虑使用它们。

// 为了避免在面向对象设计的路上被卡住，你需要时不时的拉一把。有很多其他的方法。值得我们花时间去研究它们。

// 回到顶部
// 7. 总结

// 虚函数的替换方法包括NVI用法和策略设计模式的其他不同的形式。NVI用法本身是模板方法设计模式的一个例子。
// 将功能从成员函数移到类外函数的一个缺点是非成员函数不能再访问类的非public成员。
// Tr1::function对象的行为就像一个泛化函数指针。这种对象支持同给定目标签名相兼容的所有可调用实体。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 36 永远不要重新定义继承而来的非虚函数
// 1. 为什么不要重新定义继承而来的非虚函数——实际论证
// 假设我告诉你一个类D public继承类B，在类B中定义了一个public成员函数mf。Mf的参数和返回类型并不重要，所以假设它们都是void。实现如下：

class B
{
public:
    void mf();
};

class D : public B
{
}

// 我们不需要了解B,D或者mf的任何细节，考虑一个类型D的对象x,
D x; // x is an object of type D
// 你会感到很吃惊，如果下面的语句：
B *pB = &x; // get pointer to x
pB->mf();   // call mf through pointer

// 同下面的语句行为不一样
D *pD = &x; // get pointer to x
pD->mf();   // call mf through pointer

// 因为两种情况中你都触发了对象x的成员函数mf。因为两种情况使用了相同的对象和相同的函数，行为就应该是相同的，不是么？
// 应该是这样。但是可能也不是这样。特别的，如果mf是非虚函数并且D定义了自己的mf版本的情况下，上面的行为会不一样：

class D : public B
{
public:
    void mf(); // hides B::mf; see Item 33
};
pB->mf(); // calls B::mf
pD->mf(); // calls D::mf

// 这种两面性行为的原因是像B::mf和D::mf这样的非虚函数都是静态绑定的(statically bound Item 37)。这意味着因为pB被声明为指向B的指针，通过pB触发的非虚函数都会是定义在类B上的函数，即使pB指向的是B的派生类对象，也就是这个例子中所实现的那样。
// 而虚函数是动态绑定的(dynamically bound Item 37)，所以它们不会有上面的问题。如果mf是一个虚函数，无论通过pB或者pD对mf进行调用都会触发D::mf，因为pB或者pD真正指向的是类型D的对象。
// 如果你实现一个类D并且重新定义继承自类B的非虚函数mf，D对象也会表现出不一致的行为。特别的，通过D对象调用mf函数的行为可能会像B也可能像D，而对象本身并不是决定因素，决定因素是指向D对象的指针类型。引用所展示出来的行为会同指针一样。

// 回到顶部
// 2. 为什么不要重新定义继承而来的非虚函数——理论论证
// 上面只是实际论证。我知道你真正想了解的是“不要重新定义继承而来的非虚函数”的理论证明。看下面的分析：
// Item 32解释了pulibc继承意味着”is-a”，Item34中描述了为什么在一个类中声明一个非虚函数就是为这个类建立了一个特化上的不变性（invariant over specialization）。如果你将这些观察应用到类B或者D以及非虚成员函数B::mf上，你会发现：
// 应用在类B上的任何事情同样能够应用在类D上，因为每个D对象都是一个B对象
// 派生自类B的类同时继承了mf的接口和实现，因为mf是B中的非虚函数。
// 现在，如果D重新定义了mf，你的设计就会出现矛盾。如果D真的想实现一个不同于B的mf，并且如果每个B对象——无论怎么特化——真的必须为mf使用B的实现，每个D对象都是一个B对象就不是真的了。在这种情况下，D不应该public继承自B。从另外一个方面，如果D真的必须public继承自B，并且D真的需要实现一个不同于B的mf，那么mf为B反应出来的特化上的不变性就不再为真。在这种情况下，mf应该是virtual的。最后，如果每个D真的是一个B，并且如果mf真的为B反应出了特化上的不变性，那么D真的不需要重新定义mf，也不应该尝试去这么做。
// 无论哪个观点，结论都相同，禁止重新定义一个继承而来的非虚函数。
// 3. 你应该对此条款似曾相识
// 如果阅读这个条款的时候给你一种似曾相识的感觉，可能是因为你已经读过Item7了，这个条款解释了为什么多态基类中的虚函数应该为virtual的。如果你违反了Item7中的条款（也就是你在多态基类中声明一个非虚析构函数），
// 你也会违反这个条款，因为派生类总是会重新定义继承而来的非虚函数：基类的析构函数。这对于没有定义析构函数的派生类来说也为真，因为正如Item 5中所解释的，如果你自己没有声明析构函数，编译器会为你自动生成一个。
// 从本质上来说，Item7只是这个条款的一个特例而已，尽管它足够重要到单独成为一个条款。

// 4. 总结
// 永远不要重新定义一个继承而来的非虚函数。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 37 永远不要重新定义继承而来的函数默认参数值
// 从一开始就让我们简化这次的讨论。你有两类你能够继承的函数：虚函数和非虚函数。然而，重新定义一个非虚函数总是错误的（Item 36），所以我们可以安全的把这个条款的讨论限定在继承带默认参数值的虚函数上。

// 1. 虚函数是动态绑定的，而默认参数是静态绑定的
// 在这种情况下，这个条款的验证就相当直接了:虚函数是动态绑定的，而默认参数值是静态绑定的。
// 这是什么？你说你不堪重负的脑袋已经忘记了动态绑定和静态绑定之间的区别？（为了好记，静态绑定也叫做早绑定（early binding），动态绑定也叫做晚绑定（late binding））。让我们看一下:
// 一个对象的静态类型是你已经在程序文本中声明的类型，考虑如下的类继承体系：

// a class for geometric shapes
class Shape
{
public:
    enum ShapeColor
    {
        Red,
        Green,
        Blue
    }; // all shapes must offer a function to draw themselves
    virtual void draw(ShapeColor color = Red) const = 0;
};

class Rectangle : public Shape
{
public:
    // notice the different default parameter value — bad!
    virtual void draw(ShapeColor color = Green) const;
};

class Circle : public Shape
{
public:
    virtual void draw(ShapeColor color) const;
};

// 现在考虑三个指针：
Shape *ps;                 // static type = Shape*
Shape *pc = new Circle;    // static type = Shape*
Shape *pr = new Rectangle; // static type = Shape*

// 在这个例子中，ps,pc和pr都被声明为指向shape的指针，所以它们用Shape作为它们的静态类型。注意无论shape指针真正指向的是什么对象，静态类型都是Shape*。
// 一个对象的动态类型由指针当前指向的对象类型来决定。也就是，它的动态类型表明了它的行为会是怎样的。看上面的例子，pc的动态类型是Circle*，pr的动态类型是Rectangle*。对于ps，它实际上没有动态类型，因为它还没有引用任何对象。
// 正如字面意思所表示的，在程序运行时动态类型是可以改变的，特别是通过赋值：
ps = pc; // ps’s dynamic type is now Circle*
ps = pr; // ps’s dynamic type is now Rectangle*
// 虚函数是动态绑定的，意味着哪个函数被调用是由发出调用的对象的动态类型来决定的：
pc->draw(Shape::Red); // calls Circle::draw(Shape::Red)
pr->draw(Shape::Red); // calls Rectangle::draw(Shape::Red)
// 这些都是旧知识了，我知道你肯定了解虚函数。当你考虑带默认参数值的虚函数时，麻烦出现了，因为虚函数是动态绑定的，但是默认参数是静态绑定的。这意味着你可能会终止一个虚函数的调用，因为函数定义在派生类中却使用了基类中的默认参数：
pr->draw(); // calls Rectangle::draw(Shape::Red)!
// 在这种情况中，pr的动态类型是Rectangle*，所以Rectangle的虚函数被调用，这也是你所期望的。在Rectangle::draw中，默认参数值是Green。然而因为pr的静态类型是Shape*,
// 这个函数调用的默认参数值是来自于Shape类而不是Rectangle类！最后的结果是这个调用由一个奇怪的也几乎是你意料不到的组合组成：也即是Shape类和Rectangle类中的draw声明混合而成。
// Ps,pc和pr都为指针不是造成这个问题的原因。如果它们是引用也同样会出现这个问题。唯一重要的事情是draw是一个虚函数，并且默认参数中的一个在派生类中被重新定义了

// 2. C++为什么不对参数进行动态绑定？
// 为什么C++坚持用一种反常的方式来运行？答案和运行时效率相关。如果一个默认参数是动态绑定的，编译器就需要用一种方法在运行时为虚函数参数确定一个合适的默认值，比起当前在编译期决定这些参数的机制，它更慢更加复杂。
// 做出的决定是更多的考虑了速度和实现的简单性，结果是你可以享受高效的执行速度，但是如果你没有注意到这个条款的建议，你就会很迷惑。

// 3. 个例讨论——为基类和派生类提供相同的默认参数
//  这都很好，但是看看如果这么做会发生什么：遵守这个条款的规定并且为基类和派生类函数同时提供默认参数：

class Shape
{
public:
    enum ShapeColor
    {
        Red,
        Green,
        Blue
    };
    virtual void draw(ShapeColor color = Red) const = 0;
};

class Rectangle : public Shape
{
public:
    virtual void draw(ShapeColor color = Red) const;
};

// 代码重复的问题出现了。更糟糕的是，与代码重复问题便随而来的代码依赖问题：如果Shape中的默认参数被修改了，所有重复这个参数的派生类都需要被修改。否则重新定义继承而来的默认参数值的问题会再度出现。该怎么做？
// 当你让虚函数按照你的方式来运行时遇到了麻烦，考虑替代设计方法是很明智的，Item 35中介绍了替换虚函数的不同方法。其中的一个是非虚接口用法（NVI idiom）：
// 用基类中的public非虚函数调用一个private虚函数，private虚函数可以在派生类中重新被定义。现在，我们用非虚函数指定默认参数，而用虚函数来做实际的工作：

class Shape
{
public:
    enum ShapeColor
    {
        Red,
        Green,
        Blue
    };
    void draw(ShapeColor color = Red) const // now non-virtual
    {
        doDraw(color); // calls a virtual
    }

private:
    virtual void doDraw(ShapeColor color) const = 0; // the actual work is done in this func
};

class Rectangle : public Shape
{
public:
private:
    virtual void doDraw(ShapeColor color) const; // note lack of a default param val
};

// 因为非虚函数应该永远不会在派生类中被重定义（Item 36），这个设计保证draw的color默认参数应该永远是Red。
// 4. 总结
// 永远不要重新定义一个继承而来的默认参数值，因为默认参数值是静态绑定的，而虚函数——你应该重新定义的唯一的函数——是动态绑定的。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Item 38 通过组合(composition)为 “has-a”或者“is-implemented-in-terms-of”建模

//  1. 什么是组合（composition）?
// 组合（composition）是一种类型之间的关系，这种关系当一种类型的对象包含另外一种类型的对象时就会产生。举个例子：

class Address
{
}; // where someone lives

class PhoneNumber
{
};

class Person
{
public:
private:
    std::string name;        // composed object
    Address address;         // ditto
    PhoneNumber voiceNumber; // ditto
    PhoneNumber faxNumber;   // ditto
};

// 在这个例子中，Person对象由string,Address和PhoneNumber对象组成。对于程序员来说，术语组合（composition）有很多替代词。像分层（layering）,包含（ containment），聚合 （aggregation）, 和植入（embedding）。

// 2. 组合关系意味着什么？
// 2.1 区分 “is-a”和“has-a”
// Item 32 中解释了public继承意味着”is-a”。组合同样有另外一个意思。事实上，有两个意思。组合即意味着“has-a”，也意味着“is-implemented-in-terms-of”。这是因为你正在处理软件中的两种不同领域（domain）。
// 你的程序中的一些对象对应着世界上的真实存在的东西，如果你要为其建模，例如，人类，车辆，视频帧等等。这样的对象是应用域（application domain）的一部分。其他的对象则是纯实现层面的人工产品。
// 例如，缓存（buffers）,互斥信号量（mutexs），搜索树（search trees）等等。这种类型的对象对应着你软件里的实现域（implementation domain）。
// 当组合关系发生在应用域中的对象之间时,它表示的是“has-a”关系。当发生在实现域中时，它表示的是“is-implemented-in-terms-of”关系。

// 上面的Person类表示的是一种“has-a”关系。一个Person对象有一个名字，一个地址和一个语音和传真电话号码。你不能说一个人“is-a”名字或者一个人“is-a”地址。你会说一个人“has-a”名字和“has-an”地址。
// 大多数人能够很容易区分这些，因此很少有人会混淆“is-a”和“has-a”的意思。

// 2.2 区分 “is-a”和“is-implemented-in-terms-of”
// 更麻烦的是对“is-a”和“is-implemented-in-terms-of”进行区分。举个例子，假设你需要一个类模板表示很小的对象set，也即是没有重复元素的collections。
// 因为重用是美好的事情，你的第一直觉就是使用标准库的set模板。当有一个已经被实现好的模板时你为什么要自己手动实现一个呢？
// 不幸的是，set的实现对于其中的每个元素都会引入三个指针的开销。因为set通常作为一个平衡搜索树来实现，这能保证将搜索，插入和删除的时间限定在对数级别（logarithmic-time）。
// 当速度比空间重要时，这是个合理的设计，但是对于你的应用，空间比速度要更重要。所以标准库的set没有为你的应用提供正确的权衡。你需要自己实现这个模板。
// 重用仍然是美好的事情。作为数据结构专家，你知道实现set会有很多选择，其中一个是使用linked lists。你同样知道标准C++库有一个list模板，所以你决定重用它。
// 特别情况下，你决定让你的初步实现的set模板继承list。也即是Set<T>将会继承list<T>。毕竟，在你的实现中，一个Set对象事实上是一个list对象。所以你将Set模板声明为如下：

template <typename T> // the wrong way to use list for Set
class Set : public std::list<T>
{
};

// 看上去都很好，但事实上有一些地方犯了严重错误。正如Item32中解释的，如果D 是一个B，对于B来说是真的对D来说也是真的。然而，一个list对象可能会包含重复元素，所以如果值3051被插入到Set<int>两次，
// 那么list将会包含3051的两个拷贝。相反。一个Set不可以包含重复元素，所以当3051被插入到Set<int>两次的时候，set只包含一个3051值。现在一个Set是一个List就不再为真了，因为对list对象为真的一些事情对Set对象来说不为真了。
// 因为这两个类之间的关系不是“is-a”，public继承是为这种关系建模的错误方式。正确的方式是意识到一个Set对象可以被“implemented in terms of”一个list对象：

template <class T> // the right way to use list for Set
class Set
{
public:
    bool member(const T &item) const;
    void insert(const T &item);
    void remove(const T &item);
    std::size_t size() const;

private:
    std::list<T> rep; // representation for Set data
};
//  Set成员函数的实现可以依赖list已经提供的功能和标准库的其他部分，所以实现上就简单直接了，前提是你对STL编程的基本知识很熟悉：
template <typename T>
bool Set<T>::member(const T &item) const
{
    return std::find(rep.begin(), rep.end(), item) != rep.end();
}

template <typename T>
void Set<T>::insert(const T &item)
{
    if (!member(item))
        rep.push_back(item);
}

template <typename T>
void Set<T>::remove(const T &item)
{
    typename std::list<T>::iterator it =         // see Item 42 for info on
        std::find(rep.begin(), rep.end(), item); // “typename” here
    if (it != rep.end())
        rep.erase(it);
}

template <typename T>
std::size_t Set<T>::size() const
{
    return rep.size();
}

// 这些函数足够简单，它们是inline函数的合理候选人，虽然我知道你想先回顾一下Item30中的讨论之后再做决定。
// 一些人会争论为了让Set接口更好的符合 Item18 的建议，也即是设计接口的时候满足容易被正确使用不容易被误用，Set应该遵循STL容器的惯例。但是遵循这些惯例就需要为Set增加很多工作，这会导致list和Set之间的关系模糊不清。
// 既然关系是这个条款的关键点，我们会为了更好的阐述而牺牲STL兼容性。此外，Set接口不应该使Set的无可争辩的正确行为黯然失色：这个权利是它和List之间的关系。这个关系不是”is-a”(虽然一开始看起来像)，而是”is-implemented-in-terms-of”。

// 3. 总结
// 组合和public继承的意义完全不同
// 在应用域中，组合意味了“has-a”，在实现域中，它意味着“is-implemented-in-terms-of”

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 39 明智而谨慎的使用private继承
// 1. private 继承介绍
// Item 32表明C++把public继承当作”is-a”关系来对待。考虑一个继承体系，一个类Student public 继承自类Person，如果一个函数的成功调用需要从Student到Person的隐式转换，
// 这时候“is-a”关系就出现了。对于一部分实例，使用private继承来代替public继承也是有价值的事情：

class Person
{
};
class Student : private Person
{
}; // inheritance is now private

void eat(const Person &p);    // anyone can eat
void study(const Student &s); // only students study

Person p;  // p is a Person
Student s; // s is a Student
eat(p);    // fine, p is a Person
eat(s);    // error! a Student isn’t a Person
// 很清楚的是，private继承并不意味着“is-a”关系。那它意味着什么呢？
// 在我们看到结果之前，先让我们看一下private继承的行为。Private继承的第一条规则也是刚才从实际中看到的:与public继承相反，如果类之间的继承关系是privte继承，编译器不会将派生类对象（Student）转换成为基类对象（Person）。
// 这也是为什么为对象s调用eat会失败。第二条规则是即使在基类中的成员是protected或者public的，从此基类中private继承而来的成员会变成派生类中的private成员。
// 这就是private继承所表现出来的行为。我们也从中看到了结论：private继承意味着“is-implemented-in-terms-of”。如果你让类D private继承自类B，你这么做是因为你想利用类B中的一些让你感兴趣的性质，
// 而不是因为在类型B和类型D之前有任何概念上的关系。因此，private继承纯粹是实现上的技术。（这也是为什么你从private基类中继承而来的任何东西在你的类中都变为了private的：所有的都只是实现上的细节。）
// 使用Item34中引入的术语，private继承意味着只是继承了实现；而接口应该被忽略掉。如果类D private继承自类B，就意味着D对象的实现依赖于类B对象，没有别的意思了。Private继承在软件实现层名才有意义，在软件设计层面是没有意义的。

// 2. 如何在private继承和组合之间做出选择
// Private继承意味着“is-implemented-in-terms-of”的事实会让你感觉有一些不安，因为Item 38中指出组合（composition）也同样意味着“is-implemented-in-terms-of”。
// 怎么在它们之间做出选择？答案是简单的：在任何可能的时候使用组合（composition），在必须使用private继承的时候才去使用它。
// 什么时候必须使用？主要是当protected成员或者（和）虚函数被牵扯进来的时候，也有处在边界的情况，因为空间原因而不能使用private继承。我们过后再去担心它，毕竟它是处在边界的情况。

// 2.1  一个不能简单的使用public继承的例子
// 假设我们正在一个涉及到Widgets类的应用上工作，我们想更好的理解Widgets是如何被使用的。例如，我们不但想知道Widget成员函数的调用有多频繁，我们同样想知道函数调用随时间变化的频率变化情况。
// 程序在不同的执行阶段会有不同的行为轮廓。举个例子，编译器对函数的使用会大大的不同于优化和代码生成时对函数的使用。

// 我们决定修改Widget类来对每个成员函数的调用次数进行追踪。在运行时，我们会定期来检查这项信息，可能也会伴随着检查每个Widget对象值和其它一些我们认为有用的数据。为了达到这个目的，
// 我们会创建一个定时器于是我们可以知道什么时候去收集这些统计信息。
// 我们更乐意去重用代码而不是实现新代码，我们翻阅了工具集，很高兴的找到了如下的类：

class Timer
{
public:
    explicit Timer(int tickFrequency);
    virtual void onTick() const; // automatically called for each tick
};

// 这正是我们要找的。我们可以为这个Timer对象配置任意的tick频率，在每个tick发生的时候，它会调用一个虚函数。我们可以重定义这个虚函数来检查Widget世界的当前状态。非常完美！
// 为了让Widget在Timer中重定义一个虚函数，Widget必须继承自Timer。但public继承是不合适的。因为Widget不是一个Timer。Widget客户不应该在一个Widget对象上调用onTick，因为onTick不是Widget的接口。
// 并且允许这样的函数调用会使得客户很容易出现对Widget接口的误用，这很明显的违反了Item 18的忠告：使接口容易被正确使用不容易被误用。Public继承在这里不是有效选择。
// 2.2 使用private继承
// 所以我们在这里使用private继承：

class Widget : private Timer
{
private:
    virtual void onTick() const; // look at Widget usage data, etc.
}

// 凭借private继承的力量，Timer的public onTick函数在Widget中变为了private，我们将其放在private关键字下并对其进行了重新声明。
// 2.3 使用组合（compostion）以及两个优点
// 这是个很好的设计，但如果private继承不是必须的，它就没有任何价值。如果我们决定使用组合（compostion）来替代private继承。我们可以在Widget内部声明一个内嵌类，此类public继承Timer,在Timer中重新定义onTick，
// 然后在Widget中声明一个此类型的对象。下面是这个方法的实现：

class Widget
{
private:
    class WidgetTimer : public Timer
    {
    public:
        virtual void onTick() const;
    };
    WidgetTimer timer;
}

// 这个设计比private继承更加复杂，因为它同时涉及到(public)继承和组合（composition）,同时引入了一个新类（WidgetTimer）。我用这个例子是提醒你如果有多种方法来处理一个设计问题，训练自己考虑多种方法是值得的（Item 35）。
// 然而，我能想出两个原因来证明使用public继承加组合比private继承更好。
//  私有继承(private):私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员，并且不能被这个派生类的子类所访问
// 第一，  你可能想使用Widget作为其他类的基类，但是你可能想阻止派生类重新定义onTick。如果Widget继承自Timer，这是不可能的，即使继承是private继承。
// （回忆一下Item 35,即使虚函数是private的，派生类还是可能重新定义它）但是如果WidgetTImer在Widget中是private的，并且继承自Timer,Widget的派生类就没有对WidgetTimer的访问权，也就不能继承它或者重新定义它的虚函数。
// 如果你使用java或者C#，并且发现C++没有阻止派生类重定义虚函数的能力（Java使用final methods，C#使用sealed），现在你有方法在C++中对此行为进行模拟了。
// 第二，  你可能想最小化Widget的编译依赖性。如果Widget继承自Timer，当Widget被编译的时候必须能够得到Timer的定义，所以定义Widget的文件必须#include Timer.h。
// 从另外一个角度讲，如果将WidgetTimer移出Widget并且Widget只包含一个指向WidgetTimer的指针，在Widget中对WidgetTimer进行简单的声明就可以了，不需要#include与Timer相关的任何头文件。
// 对于大型系统来说，这样的解耦是很重要的。（编译依赖的详细介绍看Item 31）

//  2.4 使用private继承比组合更加合理的例子
// 早些时候我指出来在派生类想要访问基类的protected部分或者想去重定义基类的虚函数的时候private继承才是有用的，但是类之间的关系是”is-implemented-in-terms-of”而不是“is-a”。
// 然而，我同时指出有一种涉及到空间优化的边缘情况可以促使你更加喜欢private继承而不是composition（组合）。
// 这种边缘情况确实靠边缘：它只应用在没有数据的类中。这种类没有非静态数据成员；没有虚函数（因为虚函数的存在会为每个对象添加一个vptr指针，见Item 7）；
// 没有虚基类（因为这样的基类同样会引入间接费用，见Item40）。从概念上来说，这样的空类对象应该不使用空间，因为对象中没有数据需要保存。然而由于技术的原因，C++使得独立对象必须占用空间，所以如果你写下下面的代码：

class Empty
{
}; // has no data, so objects should use no memory

class HoldsAnInt
{ // should need only space for an int
private:
    int x;
    Empty e; // should require no memory
};

// 你会发现sizeof(HoldsAnInt)>sizeof(int):一个Empty数据成员也会占用空间。对于大多数编译器来说，sizeof(Empty)为1，因为C++法则处理大小为0的独立对象时会默认向” empty ”对象中插入一个char。
// 然而，内存对齐的需求（见Item 50）可能导致编译器向HoldsASnInt这样的类中添加填充物，所以HoldsAnInt对象不会只多出来一个char的大小，实际上会增加足够的空间来容纳第二个int。（在我测试过的所有编译器中，上面描述的填充也确实发生了。）
// 但是可能你注意到了我非常小心的说明是“独立”（freestanding）对象占用的空间必须不能为0。这个限制不能被应用在派生类对象的基类部分中，因为他们不是“独立“的。如果你继承自Empty类而不是包含一个Empty类型的对象，

class HoldsAnInt : private Empty
{
private:
    int x;
};

// 你就会发现sizeof(HoldsAnInt) == sizeof(int)。这被称作EBO（empty base optimization）, 并且我测试过的编译器都通过了这个测试。如果你是一个库开发人员，如果其客户对空间十分关心，那么了解一下EBO是很值得的。并且你需要知道EBO一般只在单继承下才是可行的。管理C++ 对象布局的规则通常意味着EBO不能被应用在有多个基类的派生类中。
// 事实上，“empty“类不是真的空。虽然它们永远不会拥有非静态数据成员，它们通常会包含typedefs,
// enums，静态数据成员或者非虚函数。STL在技术上有很多包含有用成员（通常为typedefs）的空类，包括基类unary_function和binary_function，用户定义的函数对象会继承这些类。多亏了EBO的广泛使用，使得这些继承很少会增加派生类的大小。

// 2.5 结论
// 让我们回到基本议题。因为大多数类不是空的，所以EBO不是使用private继承的合法理由。进一步来说，大多数继承对应着”is - a”，这也是public继承的工作而不是private继承。
// 组合和private继承都意味着“is - implemented - in - terms - of“，但是组合更容易理解，所以你应该在任何可能的情况下使用它。

// 当你处理两个类时，它们不是“is - a“的关系，一个类要么需要访问另外一个类的protected成员要么需要重新定义一个或多个它的虚函数，这时候private继承在大多数情况下会是合法的设计策略。
// 即使在这种情况中，我们看到public继承和包含（containment）的混合使用通常情况下能够产生我们需要的行为，虽然增加了设计复杂性。
// 明智而谨慎的使用private继承就意味着，在你考虑过所有的替代方法之后，在你的软件中它是表示两个类关系的最好方法，在这种情况下才去使用它。

// 3. 总结 Private继承意味着“is - implemented - in - terms - of “。它通常比组合的使用要低一个层次，但是当派生类需要访问protected基类成员或者需要重新定义继承而来的虚函数时使用Private继承有意义的。
// 不像组合，private继承能够使用空基类优化。这对努力减少对象大小的库开发者来说很重要。



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Item 40 明智而谨慎的使用多继承
//  1. 多继承的两个阵营
// 当我们谈论到多继承（MI）的时候，C++委员会被分为两个基本阵营。一个阵营相信如果单继承是好的C++性质，那么多继承肯定会更好。另外一个阵营则争辩道单继承诚然是好的，但多继承太麻烦，而不值得去使用它。
// 在这个条款中，我的主要目标是让你明白多继承的这两个观点。
// 2. 从多个基类中继承的名字不能相同
// 第一件事情是你需要认识到使用MI进行设计时，从多个基类中可能会继承相同的名字（例如函数或者typedef等等）。这就会导致模棱两可的问题，例如：

class BorrowableItem {      // something a library lets you borrow
public:
void checkOut();                // check the item out from the library
};

class ElectronicGadget {
private:
bool checkOut() const; // perform self-test, return whether test succeeds
};

class MP3Player{/ note MI here
public BorrowableItem,    // (some libraries loan MP3 players)
public ElectronicGadget
};               // class definition is unimportant

MP3Player mp;
mp.checkOut();          // ambiguous! which checkOut?

// 注意在这个例子中，对checkout的调用是模棱两可的，即使只有两个函数中的一个是可访问的。（checkout在BorrowableItem中是public的而在ElectronicGadget中是private的）。
// 这对应着C++规则中对重载函数是如何解决的：在寻找是否有一个函数可被访问之前，C++首先首先识别出函数调用的最佳匹配函数。找到最佳匹配函数之后才会检查函数的可访问性。在这种情况下，搜寻名字的时候checkOut是模棱两可的，
// 所以既不能解决函数重载的问题也不能决定最佳匹配函数。ElectronicGadget::checkOut的可访问性根本不会被检查到。
// 为了解决这个模棱两可的问题，你必须指定调用哪个基类的函数：
mp.BorrowableItem::checkOut();   // ah, that checkOut

// 当然你也可以显示调用ElectronicGadget::checkOut，但是模棱两可的错误将会被下面的错误所替代——“你在尝试调用一个private成员函数”
// 3. 钻石型多继承
// 3.1 钻石型继承中数据成员的重复问题
// 多继承仅仅意味着从多个基类（多于一个）中继承，但是对于多继承来说在继承体系中发现更高层次的基类也并不是不常见。这就导致了我们常说的致命的“钻石型多继承”：

class File{};
class inputfile:public File{};
class outputfile:public File();
class IOFile:public inputfile,public outputfile{};

// 任何时候在一个继承体系中，如果基类和派生类之间的路径多于一条，你必须面对基类中的数据成员是否在每条路径上都要被复制的问题。例如，假设File类有一个数据成员，fileName。\
// IOFile应该有它的几份拷贝？从一方面讲，它从每个基类中都继承了一份拷贝，所以表明IOFile应该会有两个fileName数据成员。从另外一方面讲，一个IOFIle只有一个文件名，所以从两个基类中继承的fileName部分不应该被重复。

// 3.2 C++如何处理钻石型继承
// C++在这场辩论中没有任何立场。它很高兴的支持两种选择，默认的选择是执行重复。如果这不是你想要的，你必须将包含数据（也即是File）的类变为虚基类。为了达到这个目的，你会对所有继承自它的类使用虚继承。
// 标准C++库包含一个多继承体系，就像上面的继承一样，但是类模版的类不在其中，这些类的名字是basic_ios，basic_istream，basic_ostream和basic_iostream,它们分别替换了File，InputFile，OutputFile和IOFile。

class File{};
class inputfile:virtual public File{};
class outputfile:virtual public File();
class IOFile:public inputfile,public outputfile{};

// 3.3 虚继承耗费资源，不能随意使用
//  从正确行为的角度，public继承应该总是virtual的。如果你只考虑这一点，规则会很简单：在你使用public继承的任何时候都要使用virtual public继承。但正确性不是我们要唯一关注的，为了阻止对继承而来的字段进行重复，
// 编译器会在背后耍一些花招，结果是使用虚继承的类创建出来的对象会比不使用虚继承的类创建出来的对象要大。访问虚基类中的数据成员比访问非虚基类中的数据成员要慢。详细情况随编译器的不同而不同，但是基本的观点很清楚：虚继承耗费资源。
// 资源耗费也体现在其他方面。管理虚基类初始化列表的规则比非虚基类更加复杂，更加不直观。初始化虚基类部分的责任由继承体系中最底层的派生类承担。
// 这种规则就意味着：（1）继承自虚基类的类如果需要初始化，它们必须意识到虚基类的存在，无论这个虚基类离派生类有多远。（2）当一个派生类被添加到继承体系中的时候，它必须承担初始化虚基类的责任（无论是直接的还是间接的虚基类）。
// 我对于使用虚基类（也就是虚继承）的建议很简单。首先，不要使用虚基类，除非你需要它。默认情况下使用非虚基类。第二，如果你必须使用虚基类，尝试着不要在这些类中放置数据。
// 这样你就不必为这些类的初始化（还有赋值）规则的古怪行为进行担心了。值得注意的是,Java和.NET中的接口（在许多方面相当于C++的虚基类）是不允许包含任何数据的。

// 4. 另外一种多继承的使用场景
// 让我们看一下下面的C++接口类（见Item 31），这个类为persons建模：
class IPerson {
public:
virtual ~IPerson();
virtual std::string name() const = 0;
virtual std::string birthDate() const = 0;
};
// IPerson的客户必须依赖IPerson指针和引用来进行编程，因为抽象类不能被实例化。为了创建可以被IPerson对象操作的对象，IPerson的客户使用工厂函数（Item 31）来实例化派生自IPerson的具现类：
// factory function to create a Person object from a unique database ID;
// see Item 18 for why the return type isn’t a raw pointer
std::shared_ptr<IPerson> makePerson(DatabaseID personIdentifier);
// function to get a database ID from the user
DatabaseID askUserForDatabaseID();
DatabaseID id(askUserForDatabaseID());
std::shared_ptr<IPerson> pp(makePerson(id)); // create an object supporting the IPerson interface manipulate *pp via IPerson’s member functions

// 但是如何使用makePerson创建返回指针指向的对象呢？很清楚，必须有一些派生自IPerson具现类，使得makePerson能够对这些具现类进行实例化。
// 我们把这个类叫做CPerson。作为一个具现类，CPerson必须为继承自IPerson的纯虚函数提供一份实现。我们可以从头开始实现这个函数，但是利用现成的组件来对其进行实现更好，这些现成的组件实现了大部分或者全部的必要功能。例如，假设一个旧数据库指定的类PersonInfo为CPerson提供了它需要的最基本的东西：

class PersonInfo {
public:
explicit PersonInfo(DatabaseID pid);
virtual ~PersonInfo();
virtual const char * theName() const;
virtual const char * theBirthDate() const;
...
private:


virtual const char * valueDelimOpen() const; // see
virtual const char * valueDelimClose() const; // below
// ...
// };
// 你能够识别出来这是一个旧类，因为成员函数返回的是const char*而不是string对象。并且如果鞋是合适的，为什么不穿上呢？这个类的成员函数的名字也表明了使用结果会非常棒。

// 你现在发现了PersonInfo的设计是用来帮助打印不同格式的数据库字段的，这些字段值的开始和结尾处都由特殊的字符串进行分隔。默认情况下，字段值的开始和结束分隔符是方括号，所以值”Ring-tailed Lemur”会被格式化成下面的字符串：

// [Ring-tailed Lemur]

// 认识到方括号并不能被PersonInfo的客户普遍接受，虚函数valueDelimOpen和valueDelimClose允许派生类指定它们自己的开始和结束分隔字符串。PersonInfo的成员函数的实现会调用这些虚函数来为返回值添加合适的分隔符。使用PersonInfo::theName作为例子，代码可能像下面这样：

// const char * PersonInfo::valueDelimOpen() const
// {

// return "[";                                                                          // default opening delimiter

// }

// const char * PersonInfo::valueDelimClose() const

// {



// return "]";                                                                                      // default closing delimiter

// }

// const char * PersonInfo::theName() const

// {

// // reserve buffer for return value; because this is

// // static, it’s automatically initialized to all zeros

// static char value[Max_Formatted_Field_Value_Length];

// // write opening delimiter

// std::strcpy(value, valueDelimOpen());

// append to the string in value this object’s name field (being careful

// to avoid buffer overruns!)

// // write closing delimiter

// std::strcat(value, valueDelimClose());

// return value;

// }


// 有人可能对PersonInfo::theName的过时设计发出疑问（特使是对固定大小的static缓存的使用，有时会造成越界或者线程问题，见Item 21），但是将这种问题放到一边，我们将焦点放在下面这件事情上：theName调用valueDelimOpen来为返回的string生成开始分隔符，然后生成名字本身，最后调用valueDelimClose。

// 因为valueDelimOpen和valueDelimClose是虚函数，由theName返回的值不仅依赖于PerosnInfo同时依赖于PersonInfo的派生类。

// 作为CPerson的实现者，这是个好消息，因为当对IPerson的文档进行精读时，你发现name和birthDate需要返回没有分隔符的值。也就是一个叫做”Homer”的人，对这个名字进行函数调用会返回“Homer”而不是“[Homer]”。

// CPerson和PersonInfo之间的关系是PersonInfo恰好有一些函数使得CPerson的实现更加容易。它们的关系因此为“is-implemented-in-terms-of”,我们知道这种关系可以被表现为其它两种形式：通过组合（Item 38）和private继承（Item 39）。Item 39指出组合通常讲比Private继承要更好，但如果虚函数需要重定义，private继承就是必须的。在这种情况中，CPerson需要重新定义valueDelimOpen和valueDelimClose，所以使用组合在这里不能工作。最简单直接的解决方案是让CPerson private继承PersonInfo，虽然Item 39解释道如果多做一些工作，CPerson可以使用组合和继承的结合体来有效重定义PersonInfo的虚函数。在这里，我们使用private继承。

// 但是CPerson同样必须实现IPerson接口，这些接口为public继承所用。这也导致了合理的多继承应用：将一个接口的public接口和一个实现的private继承结合起来使用：


// class IPerson {                                      // this class specifies the

// public:                                                  // interface to be implemented

// virtual ~IPerson();

// virtual std::string name() const = 0;

// virtual std::string birthDate() const = 0;

// };



// class DatabaseID { ... };            // used below; details are
// // unimportant

// class PersonInfo {   // this class has functions


// public: // useful in implementing
// explicit PersonInfo(DatabaseID pid); // the IPerson interface
// virtual ~PersonInfo();
// virtual const char * theName() const;
// virtual const char * theBirthDate() const;
// ...
// private:
// virtual const char * valueDelimOpen() const;
// virtual const char * valueDelimClose() const;
// ...
// };

// class CPerson: public IPerson, private PersonInfo { // note use of MI
// public:
// explicit CPerson(DatabaseID pid): PersonInfo(pid) {}
// virtual std::string name() const // implementations
// { return PersonInfo::theName(); } // of the required
// // IPerson member
// virtual std::string birthDate() const // functions
// { return PersonInfo::theBirthDate(); }
// private: // redefinitions of
// const char * valueDelimOpen() const { return ""; } // inherited virtual
// const char * valueDelimClose() const { return ""; } // delimiter

// };                                                                   // functions

// 这个例子表明了多继承是有用的和可被理解的。

 

// 回到顶部
// 5. 何时使用多继承
// 最后，多继承只是面向对象工具包中的另外一个工具。和单继承相比，使用和理解都更加复杂，所以如果你碰到一个和多继承设计基本相当的单继承设计，使用单继承设计基本上会是更好的选择。如果你只能想出多继承设计，你应该更加努力的思考——肯定有一些方法来让单继承正常的工作。同时，多继承有时是最干净的，最容易维护以及最合理的，它能使工作有效进行。如果你碰到这种情况，不要害怕使用它。确保合理而谨慎的使用private继承就可以了。

 

// 回到顶部
// 6. 总结
// 多继承比单继承更加复杂。它会引起新的模棱两可的问题，因此需要使用虚继承。
// 虚继承的使用会增大体积，降低速度，增加初始化和赋值的复杂度。在虚基类中没有数据的情况下使用多继承是最实际的。
// 多继承也有合理的使用场景。一种使用场景涉及到将对接口类的public继承和将对实现类的private继承相结合的情况。



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 41 理解隐式接口和编译期多态
// 1. 显示接口和运行时多态
// 面向对象编程的世界围绕着显式接口和运行时多态。举个例子，考虑下面的类（无意义的类）

class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);                          // see Item 25
};

// 考虑下面的函数（同样没有意义），
void doProcessing(Widget& w)
{
if (w.size() > 10 && w != someNastyWidget) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}

// 对于doProcessing中的w，我们可以这样说:
// 因为w被声明为Widget类型，w必须支持Widget接口。我们可以在源码中搜寻这个接口（例如，在Widget的头文件中），以便能够确切的知道它长成什么样子，所以我将其叫做一个显式的接口（explicit interface）——可以显式的在源码中看到的接口。
// 因为Widget中的一些成员函数是虚的，w对这些函数的调用会展示出运行时多态:w具体调用哪个函数会根据运行时w的动态类型来决定。
 
// 2. 隐式接口和编译期多态
// 模板（template）和泛型编程（generic programming）的世界从根本上发生了变化。在这个世界中，显式接口和运行时多态继续存在，但是它们不再像以前那么重要。相反，隐式接口和编译时多态被挪到了前台。
// 为了了解这是什么样子的，我们将doProcessing从函数转换为一个函数模板，看看会发生什么：

template<typename T>
void doProcessing(T& w)
{
if (w.size() > 10 && w != someNastyWidget) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}

// 现在我们能对doProcessing中的w说些什么呢？
// W必须支持的接口由模板中w需要执行的操作所决定。例如，w的类型T必须支持size，normalize和swap成员函数；拷贝构造函数（来创建temp）；和不等比较（同someNastyWidget进行比较）。
// 我们很快就能发现这也不是很精确的，但是对于现在来说足够了。重要的是，这些表达式必须是T所支持的隐式接口，它们对于模板来说必须是有效的以便能够通过编译。
// 对于涉及到w的像operator>和operator!=这样的函数调用，可能涉及到模板的实例化来让这些调用成功。这些实例化在编译期发生。因为用不同的模板参数实例化出来的函数模板会导致不同的函数被调用，这叫做“编译时多态”。
 
// 显示接口和隐式接口的区别
// 显示接口的特点
// 即使你永远不使用模板，你也应该熟悉运行时多态和编译期多态的区别，因为这同编译期决定调用哪个重载函数以及运行期决定绑定哪个虚函数是类似的。隐式和显式接口的区别对于模板来说是新的概念，
// 然而，一个显式的接口由函数签名组成，也即是函数名字，参数类型，返回值类型等等。Widget类的公共接口，例如：

// class Widget {
// public:
// Widget();
// virtual ~Widget();
// virtual std::size_t size() const;
// virtual void normalize();
// void swap(Widget& other);
// };
// 由一个构造函数，一个析构函数，和函数size，normalize和swap以及参数类型，返回值类型和这些函数的常量性组成。（同样包含编译器生成的拷贝构造函数和拷贝赋值运算符——看Item 5）。它同样可以包含typedef和数据成员，如果你够大胆违反Item22的建议的话（将数据成员声明为private）。虽然在这个例子中没有这么做。

// 3.2 隐式接口的特点
// 一个隐式的接口会有很大的不同。它不是基于函数签名。而是由有效表达式组成。再看一下doProcessing模板开始部分的条件表达式：

// 1 template<typename T>
// 2 void doProcessing(T& w)
// 3 {
// 4 if (w.size() > 10 && w != someNastyWidget) {
// 5 ...
 

// T（w的类型）的隐式接口看上去会有如下限制：

// 它必须提供一个名字为size的成员函数并且返回一个整型值。
// 它必须支持!=操作符函数，能够对两个T类型的对象进行比较。（这里，我们假设someNastWidget的类型为T。）
// 多亏了操作符重载，上面的两个限制都不需要满足。T必须支持一个size成员函数，值得提及的是这个函数可能继承自一个基类。但是这个成员函数没有必要返回一个整型值。甚至不需要返回一个数字类型值。如果这么说的话，它甚至不需要返回operator>定义中所需要的值。他需要的是返回一个类型X的对象，于是可以在一个类型X对象和int（因为10是int型的）型对象上调用operator>。但是Operator>没有必要带一个类型X的参数，因为它也可以带一个类型Y的参数，只要Y可以隐式的转成X就可以了。

 

// 类似的，T也没有必要支持operator!=，因为operator!=带一个类型X的参数和一个类型Y的参数也能接受。只要T能转成X并且someNastyWidget的类型可以转换成Y，那么函数调用就是有效的。

 

// （说句题外话，这个分析没有考虑将operator&&进行重载的可能性，这样就将上面的表达式的意思从一个连接词转换成了其它的意义迥然的东西。）

 

// 大多数人当第一次开始考虑这种隐式转换就头疼，你不需要吃阿司匹林。隐式接口只是简单的由一些有效表达式组成。表达式本身看起来复杂，但是加在上面的限制一般来说是简单直接的。例如，考虑下面的条件表达式，

// 1 if (w.size() > 10 && w != someNastyWidget) ...
// 很难说要对函数size，operator>,operator&&或者operator!=做什么限制，但是很容易辨认出需要对整个表达式做出的限制。If声明的条件部分必须是一个boolean表达式，所以不管涉及到什么类型，也不管w.size() > 10 && w != someNastyWidget产生什么，它必须同bool是兼容的。这是模板doProcessing强加在类型参数T上的隐式接口的一部分。剩下的doProcessing所需要的接口就是对拷贝构造函数的调用，还有swap对于类型T来说必须是有效的。

 

// 强加在模板参数上的隐式接口同强加在类对象上的显示接口一样真实，两者都是在编译阶段检查。你不能同一个类提供的显示接口相矛盾的方式使用一个类对象（不会编译通过），你也不能随便在一个模板中尝试使用一个对象，除非这个对象支持模板需要的隐式转换（否则也不能通过编译）

// 回到顶部
// 4. 总结
// 类和模板都支持接口和多态。
// 对于类来说，接口是显示的，以函数签名为中心。多态发生在运行时，通过虚函数来实现。
// 对于模板参数来说，接口是隐式的，基于有效表达式。模板多态通过模板实例化和函数重载来实现，它发生在编译期。



////////////////////////////////////////////////////////////////////
// Item 42 理解typename的两种涵义
// 1. class和typename含义相同的例子
// 问题：在下面的模板声明中class和typename的区别是什么？

template<class T> 
class Widget;     // uses “class”2

template<typename T> 
class Widget;            // uses “typename”
 
// 答案：没有任何区别。当声明一个模板类型参数时，class和typename意味着相同的事情。一些程序员喜欢使用class，因为容易敲打
// 其他的（包括我）更加喜欢使用typename，因为用它表明参数不需要是一个class类型。一些程序员在允许使用任何type的时候使用typename，只用对用户自定义的类型使用class。
// 但是从C++ 的观点来看，在声明模板参数的时候class和typename意味着相同的事情。

// 2. 必须使用typename的例子
// 然而，C++并不总是将class和typename同等对待。有时你必须使用typename。为了理解在什么时候必须使用，我们必须讨论能够在模板中引用的两种名字。
// 假设我们有一个函数模板，用和STL兼容的容器作为模板参数，此容器中包含的对象能够被赋值给int类型。进一步假设这个函数打印容器中的第二个元素值。我在下面以愚蠢的方式实现了一个愚蠢的函数，
// 它甚至不能通过编译，但是请忽略这些事情，看下面的例子: 

template<typename C> // print 2nd element in
void print2nd(const C& container) // container;
{ 
if (container.size() >= 2) { // this is not valid C++!
C::const_iterator iter(container.begin()); // get iterator to 1st element
++iter; // move iter to 2nd element
int value = *iter; // copy that element to an int
std::cout << value;   // print the int
}
}

// 我对此函数中的两个本地变量做了高亮，iter和value。Iter的类型是C::const_iterator，它依赖于模板参数C。模板中依赖于模板参数的名字被称作依赖名字(dependent names)
// 当一个依赖名字嵌套在一个类中的时候，我把它叫做内嵌依赖名字（nested dependent name）。C::const_iterator是一个内嵌依赖名字。
// 事实上，它是一个内嵌依赖类型名字（nested dependent type name）,也即是指向一个类型（type）的内嵌依赖名字。
// 对于print2nd中的其他本地变量，value，类型为int。int不依赖于任何模板参数。这种名字被称作“非依赖名字”（non-dependent names）
// (我不知道为什么不把它们叫做独立名字（independent names）。“non-dependent”是一种不好的命名方式，但毕竟它是术语，所以需要遵守这个约定。）
// 内嵌依赖名字会导致解析困难。例如，如果我们让print2nd函数以下面的方式开始，会更加愚蠢：

template<typename C>
void print2nd(const C& container)
{
    C::const_iterator * x;
}

// 看上去像是我们声明了一个本地变量x，这个x指针指向一个C::const_iterator。但是它看上去是这样的仅仅因为我们“知道”C::const_iterator是一个type。
// 但是如果C::const_iterator不是一个type会是怎样呢？如果C有个静态数据成员恰好被命名为const_iterator会发生什么？如果x恰巧是一个全局变量的名字呢？在这种情况下，
// 上面的code就不会声明一个本地变量，它会是C::const_iterator和x的乘积！听起来有些疯狂，但这是可能的，实现C++编译器的人员也必须考虑到所有可能的输入，包括一些看起来很疯狂的例子。
// 直到C被确定之前，没有办法知道C::const_iterator是否是一个type，当函数模板print2nd被解析的时候，C不能够被确认。为了处理这种模棱两可的问题，
// C++有一个准则：如果解析器在模板中碰到了一个内嵌依赖名字，它不会认为这是一个type，除非你告诉它。默认情况下，内嵌依赖名字不是types。（对于这个规则有个例外，一会会提到。）
// 将上面的规则记在心中，再看一次print2nd的开始部分：

template<typename C>
void print2nd(const C& container)
{
if (container.size() >= 2) {
C::const_iterator iter(container.begin()); // this name is assumed to not be a type this is valid C++
}
// 现在应该清楚为什么这不是有效的C++了。Iter的声明只有在C::const_iterator是一个type的情况下才有意义，但是我们并没有告知C++它是一个类型，于是C++假设它不是一个类型。
// 为了纠正这种情况，我们必须告诉C++ C::const_iterator是一个类型。我们将typename放在type之前就能达到这个目的：


template<typename C>                                                              
void print2nd(const C& container)
{
if (container.size() >= 2) {
typename C::const_iterator iter(container.begin());
}
}
// 这个规则很简单：在一个模板中，任何时候你引用一个内嵌依赖类型名字，你都必须在名字前加上typename。（也有例外，一会会提到。）
// typename应该只被用来确认一个内嵌依赖类型名字；其他的名字不应该加这个前缀。例如，下面的函数模板使用两个参数，一个容器和一个容器的迭代器：
template<typename C>                        // typename allowed (as is “class”)
void f(const C& container, typename C::iterator iter); // typename not allowed, typename required

// C不是内嵌依赖类型名字（它没有内嵌在任何依赖于模板参数的东西中），所以在声明容器的时候不应该加typename，但是C::iterator是一个内嵌依赖类型名字，所以需要加typename。
// 3. 一个例外——不能使用typename的地方
//  ”typename”必须加在内嵌依赖类型名字之前“这个规则有一个例外：基类列表中的内嵌依赖类型名字或者成员初始化列表中的基类标识符不能加typename。例如：

template<typename T>
class Derived: public Base<T>::Nested { // base class list: typename not allowed
public:                                    
explicit Derived(int x): Base<T>::Nested(x){        // base class identifier in mem. // init. list: typename not allowed
typename Base<T>::Nested temp; // use of nested dependent type name not in a base class list or as a base class identifier in a mem. init. list: typename required
} 
};
// 这种不一致性令人感到厌烦，但是一旦你有了一点经验，你就会注意到它。

// 4. 最后的例子——为typename使用typedef
// 让我们看最后一个typename的例子，因为它代表了你将会在真实代码中看到的某些东西。假设我们正在实现一个函数模板，带了一个迭代器参数，我们想为迭代器指向的对象做一份本地拷贝，temp。
// 我们可以像下面这样实现：

template<typename IterT>
void workWithIterator(IterT iter)
{
typename std::iterator_traits<IterT>::value_type temp(*iter);
}

// 不要让 std::iterator_traits<IterT>::value_type 吓到你。这只是标准特性类（standard traits class）的一种使用方法，这是“类型IterT对象指向的类型“的C++实现方式。
// 这个句子声明了一个本地变量(temp)，它的类型同IterT对象指向的对象的类型一致，它将temp初始化为iter指向的对象。如果IterT是vector<int>::iterator，那么temp就是int类型的。
// 如果IterT是list<string>::iterator，temp就是string类型的。因为std::iterator_traits<IterT>::value_type是一个内嵌依赖类型名字
// （在iterator_traits<IterT>内部value_type是内嵌的，IterT是一个模板参数），我们必须为其添加typename。

// 如果你认为读std::iterator_traits<IterT>::value_type是一件不让人愉快的事情，想像一下将其打出来会是什么样的。如果你像大部分程序员一样，多次输入这个表达式的想法是可怕的，
// 所以你会想为其创建一个typedef。对于像value_type这样的特性（traits）成员名字来说（对于特性的信息看Item47）,使用惯例是使得typedef名字和特性成员名字相同，
// 所以这样一个本地typedef通常被定义成下面这样：

template<typename IterT>
void workWithIterator(IterT iter)
 {
 typedef typename std::iterator_traits<IterT>::value_type value_type;
 value_type temp(*iter);
}

// 许多程序员发现将“typedef typename“并列看上去不和谐，但是对于使用内嵌依赖类型名字的规则来说，这是一个合乎逻辑的结果。你会很快习惯这种用法。毕竟，你有着很强的驱动力。
// 你想输入typename std::iterator_traits<IterT>::value_type多少次呢？

// 5. Typename的执行因编译器而异
// 作为结束语，我应该提及的是关于typename规则的强制执行随着编译器的不同而不同，一些编译器接受需要typename但实际上没有输入的情况；
// 一些编译器接受输入了typename但实际上不允许的情况；还有一些（通常是老的编译器）在需要输入typename时拒绝了typename输入。
// 这就意味着typename和内嵌依赖类型名字的交互会产生让你头痛的问题。

// 6. 总结
// 当声明模板参数的时候，class和typename是可以互换的
// 使用typename来识别内嵌依赖类型名字，但在基类列表中或者成员初始化列表中的基类标识符除外


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 43 了解如何访问模板化基类中的名字
// 1. 问题的引入——派生类不会发现模板基类中的名字
// 假设我们需要写一个应用，使用它可以为不同的公司发送消息。消息可以以加密或者明文（未加密）的方式被发送。如果在编译阶段我们有足够的信息来确定哪个信息会被发送到哪个公司，
// 我们可以使用基于模板的解决方案：

class CompanyA {
public:
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
};

class CompanyB {
public:
void sendCleartext(const std::string& msg);
void sendEncrypted(const std::string& msg);
};

                                                      // classes for other companies
class MsgInfo {};                            // class for holding information
// used to create a message

template<typename Company>
class MsgSender {
public:      
void sendClear(const MsgInfo& info) // ctors, dtor, etc.
{
    std::string msg;
    create msg from info;
    Company c;
    c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& info)           // similar to sendClear, except calls c.sendEncrypted
{}                                              

}

// 这会工作的很好，但是假设有时候我们需要在发送信息之前log一些信息。一个派生类就能够很容易的添加这些信息，下面的实现看上去是合理的实现方式：

template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:         // ctors, dtor, etc.
void sendClearMsg(const MsgInfo& info)
{
// write "before sending" info to the log;
sendClear(info); // call base class function;
// this code will not compile!
// write "after sending" info to the log;
}
};

// 注意派生类中的消息发送函数和基类相比（sendClear）是一个不同的名字（sendClearMsg）。这是好的设计，因为这避免了隐藏继承而来的名字的问题（Item 33），同时避免了重新定义继承而来的非虚函数问题（Item 36）。但是代码不能通过编译，至少符合标准的编译器不能通过编译。这些编译器会发出sendClear不存在的抱怨。我们能够看到sendClear是在基类中，但是编译器没有在基类中发现它。我们需要知道为什么。

// 回到顶部
// 2. 问题分析
// 2.1 一般化分析
// 问题出现在当编译器遇到类模版LoggingMsgSender的定义时，它们不知道它继承自什么类。当然，它是继承自MsgSender<Company>，但是Company是一个模板参数，这个参数只有在LoggingMsgSender被实例化的时候才会被确认。在不知道Company是什么的情况下，我们也不知道MsgSender<Company>是什么样子的。因此也就没有方法获知是否存在sendClear函数。

// 2.2 用实例来证明问题所在
// 为了使问题更加具体，假设我们有一个类CompanyZ使用加密的方式进行通信：


// class CompanyZ {                                               // this class offers no

// public:                                                                // sendCleartext function

// ...

// void sendEncrypted(const std::string& msg);

// ...

// };

// 普通的MsgSender模板对于CompanyZ来说是不合适的，因为普通模板提供了一个对于CompanyZ对象来说没有意义的函数。为了改正这个问题，我们能够为CompanyZ创建一个MsgSender的特化版本：

// template<>                                     // a total specialization of

// class MsgSender<CompanyZ> {      // MsgSender; the same as the


// public: // general template, except
// ... // sendClear is omitted
// void sendSecret(const MsgInfo& info)
// { ... }

// 注意在类定义开始的地方出现的”template<>” 语法。它表明这既不是模板也不是单独的类。它是当使用CompanyZ作为模板参数时，会使用到的MsgSender模板的特化版本。这叫做模板全特化（total template specialization）：模板MsgSender为类型CompanyZ进行了特化，并且特化是全特化——一旦类型参数被定义为ComanyZ，模板参数的其它地方就不会再发生变化。

 

// 在MsgSender已经有了CompanyZ的特化版本的情况下，再看一下派生类LoggingMsgSender:

// template<typename Company>
// class LoggingMsgSender: public MsgSender<Company> {
// public:
// ...
// void sendClearMsg(const MsgInfo& info)
// {
// write "before sending" info to the log;
// sendClear(info); // if Company == CompanyZ,
// // this function doesn’t exist!
// write "after sending" info to the log;
// }
// ...
// };

// 正如注释中所写的，当基类是MsgSender<CompanyZ>的情况下这段代码没有意义，因为基类中没有提供sendClear函数。这也是C++拒绝这个调用的原因：它认识到基类模板可能被特化了，但是特化版本并没有提供普通模板中的一般接口。因此，它拒绝在模板化基类中寻找继承而来的名字。从某种意义上讲，当我们从面向对象C++转到模板C++的时候（Item 1），继承就会停止工作。


// 3. 如何解决问题——三种方法
 

// 如果让其重新工作，我们必须让C++“不在模板化基类中寻找“的行为失效。有三种方法达到这个目标。

// 第一，调用基类函数时你可以为其加上”this->“：


// template<typename Company>
// class LoggingMsgSender: public MsgSender<Company> {
// public:
// ...
// void sendClearMsg(const MsgInfo& info)
// {
// write "before sending" info to the log;
// this->sendClear(info); // okay, assumes that
// // sendClear will be inherited
// write "after sending" info to the log;
// }
// ...
// };

// 第二，你可以使用using声明，你可能会熟悉，因为Item 33中用了类似的解决方案。那个条款中解释了如何使用using声明来将隐藏起来的基类名字带入派生类作用域。我们于是可以像下面这种方式实现sendClearMsg:

// template<typename Company>
// class LoggingMsgSender: public MsgSender<Company> {
// public:
// using MsgSender<Company>::sendClear; // tell compilers to assume
// ... // that sendClear is in the
// // base class
// void sendClearMsg(const MsgInfo& info)
// {
// ...

// sendClear(info);                                  // okay, assumes that

// ...                                                          // sendClear will be inherited

// }

// ...

// };


// 最后，让你的代码通过编译的方法是在基类中明确指出需要调用的函数：

// template<typename Company>

// class LoggingMsgSender: public MsgSender<Company> {

// public:

// ...

// void sendClearMsg(const MsgInfo& info)

// {

// ...

// MsgSender<Company>::sendClear(info);

// // okay, assumes that


// ... // sendClear will be
// } // inherited
// ...
// };

// 这基本上会是你最不愿意使用的解决这个问题的方法，因为如果被调用的函数是virtual的，显示的限定符会关掉virtual绑定行为。

 

// 从名字可见性的观点来看，每个方法都做了同样的事情：它向编译器许诺，接下来的任何基类模板特化都会支持一般模板提供的接口。这样的许诺是当所有的编译器解析一个像LoggingMsgSender的派生类模板的时候所需要的，但是如果这个许诺并没有兑现，在接下来的编译中真理就会浮现。例如，如果下面的源码有这种情况：

// 1 LoggingMsgSender<CompanyZ> zMsgSender;
// 2 MsgInfo msgData;
// 3 ...                                                                  // put info in msgData
// 4 
// 5 zMsgSender.sendClearMsg(msgData);         // error! won’t compile
 

// 对sendClearMsg的编译不会通过，因为从这个点上来说，编译器知道基类是模板的特化版本MsgSender<CompanyZ>,并且它们知道这个类没有提供sendClearMsg想要调用的sendClear函数。

 

// 回到顶部
// 4. 本条款讨论的根本所在
 

// 从根本上来说，这个问题是编译器对基类成员的无效引用进行诊断的早（当派生类模板被解析的时候）或晚（当这些模板用特定的模板参数进行实例化的时候）的问题。C++的方针是更加喜欢早点诊断，这也是为什么当类从模板中特化的时候，它假定对基类的内容一无所知。

 

// 回到顶部
// 5. 总结
// 在派生类模板中，引用基类模板中的名字可以使用“->this“前缀，通过使用using声明，或者通过使用显示的使用基类限定符。


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 44 将与模板参数无关的代码抽离出来

// 1. 使用模板可能导致代码膨胀
// 使用模板是节省时间和避免代码重用的很好的方法。你不需要手动输入20个相同的类名，每个类有15个成员函数，相反，你只需要输入一个类模板，然后让编译器来为你实例化20个特定的类和300个你需要的函数。
// (只有在被使用的情况下类模版的成员函数才会被隐式的实例化，所以只有在300个函数被实际用到的情况下才会生成300个成员函数。）函数模板同样吸引人。
// 你不用手动实现许多函数，你只需要实现一个函数模板，然后让编译器来做余下的事情。
// 然而在有些时候，如果你不小心，使用模板会导致代码膨胀（code bloat）:产生重复代码或者数据的二进制文件，或者两者都有。结果可能是源码看起来合身整齐，但是目标代码（object code）臃肿松弛。臃肿松弛很不好，因此你需要知道如果避免这样的二进制浮夸。

// 2. 共性和可变性分析
//  你的主要工具有着很威风的名字：共性和可变性分析（commonality and variability analysis），但是这个概念很平常。即使在你的编程生涯中从未实现过一个模板，你也总是会做这样的分析。
// 2.1 函数和类中的代码重复分析
// 当你正在实现一个函数，你意识到函数实现的某些部分同另外一个函数实现基本上是相同的 ，你会重复这些代码么？当然不会。你将两个函数的公共代码提取出来，放进第三个函数中，然后在两个函数中调用这个新函数。
// 总结一下就是，你对两个函数进行分析，找到相同和不同的部分，将相同的部分移到一个新的函数中去，将不同的部分保留在原来的函数中。类似的，如果你正在实现一个类，你意识到类中的一部分另一个类中的一部分是相同的，你不应该重写相同的部分。
// 相反，你可以将相同的部分移到一个新类中，然后使用继承或者组合（Item 32,Item 38,Item 39）让原始类访问共同的特性。原始类中不同的部分仍然保留在原来的位置。

// 2.2 模板中的代码重复分析及消除重复方法
// 当实现模板的时候，你也会做相同的分析，你会使用相同的方式来阻止重复，但是这里有一个让你伤痛的地方。在非模板(non-template)代码中，重复是显示的：你可以看到在函数之间或者类之间会有代码重复。
// 在模板代码中，重复是隐式的：只有一份模板源码，所以你必须训练你自己当一个模板被实例化多次的时候，你能够感觉到重复会不会发生。

// 2.2.1 消除代码膨胀第一关——去掉非类型参数
// 例如，假设你想为固定大小的矩阵实现一个模板，需要支持矩阵的转置。
 
 template<typename T, std::size_t n> 
// template for (n x n) matrices of objects of type T; see below for info on the size_t parameter
class SquareMatrix { 
public:
void invert();  // invert the matrix in place
};

// 这个模板带了一个类型参数，T，但是也带了一个类型size_t的参数，一个非类型（non-type）参数。非类型参数比类型参数少了共性，但是它们是完全合法的，并且在这个例子中，它们也能非常自然。
// 现在考虑下面的代码：
SquareMatrix<double, 5> sm1;
sm1.invert(); // call SquareMatrix<double, 5>::invert
SquareMatrix<double, 10> sm2;
sm2.invert();  // call SquareMatrix<double, 10>::invert

// 在这里将会实例化invert的两份拷贝。这两个函数并不相同，因为一个在5*5的矩阵上工作，另外一个在10*10的矩阵上工作，但是如果不考虑常量5和10，这两个函数将会是一样的。这是使得包含模板的代码出现膨胀的典型方式。
// 如果你看到两个函数，它们的所有字符都是相同的，除了一个版本使用5而另外一个版本使用10，你接下来会做什么？你的直觉是会创建一个带一个参数的函数版本，然后以5或者10为入参调用这个函数而不是重复代码。
// 你的直觉能够很好的为你服务！这是实现SquareMatrix的第一关：

template<typename T> // size-independent base class for
class SquareMatrixBase { // square matrices
protected:
void invert(std::size_t matrixSize); // invert matrix of the given size
};

template<typename T, std::size_t n>
class SquareMatrix: private SquareMatrixBase<T> {
private:
using SquareMatrixBase<T>::invert; // make base class version of invert
public:
void invert() { invert(n); } // make inline call to base class version of invert
}; 

// 正如你所看到的，带参数的invert版本被放在基类SquareMatrixBase中。像SquareMatrix一样，SquareMatrixBase是一个模板，但是与SquareMatrix不同的是，它在矩阵中只对对象类型进行模板化。
// 因此，包含一个给定类型对象的所有矩阵将会分享一个单一的SquareMatrixBase类。这样它们会分享SquareMatrixBase类的invert版本的单一拷贝。
// （你不能将其声明为inline，因为一旦被inline了，每个SquareMatrix::invert的实例都会得到SquareMatrixBase::invert代码的一份拷贝（看Item 30）,你会发现你有回到了对象代码重复的原点。）
// SquareMatrixBase::invert只被用来在派生类中防止代码重复，所以是protected而不是public的。调用它的额外开销应该是0，因为派生类的inverts调用基类版本使用了inline函数。
// （inline是隐式的 见Item 30）同时注意SquareMatrix和SquareMarixBase之间的继承是private的。
// 这精确的反映出一个事实：使用基类的唯一原因是帮助派生类的实现，并非表达出SquareMatrix和SquareMatrixBase之间的“is-a”关系。(private继承是 has-a )（有关private继承的信息，见Item 39）

// 2.2.2 消除代码膨胀第二关——派生类如何告知基类数据在哪里
// 到现在为止看上去都很好，但是还有一个我们没有处理的棘手的问题。SquareMatrixBase::invert如何知道在什么数据上进行操作？它从参数中得知矩形的大小，但是它如何知道为特殊矩阵提供的数据在哪里？大概只有派生类才会知道。
// 派生类如何同基类进行通讯才能让基类执行invert?
// 一个可能的方法是向SquareMatrixBase::invert中添加另外一个参数，可能是一个指向一块内存的指针，内存中存放矩形数据。这种方法可以工作，但是十有八九，
// invert不是存在于SquareMatrix中的能够以独立于size的方式重写的，并且移入SquareMatrixBase中的唯一函数。如果有几个这样的函数，我们就需要一种方法能够找到存放矩形数据的内存，
// 我们可以为所有的函数添加一个额外的参数，但是如此以来我们就重复告诉了SquareMatrixBase同样的信息。这看上去是错误的。
// 一个替换方法是让SquareMatrixBase存储一个指向存放矩形数据的内存的指针。这同存放矩形大小有相同的效果。结果如下：

template<typename T>
class SquareMatrixBase {
protected:
SquareMatrixBase(std::size_t n, T *pMem) // store matrix size and a ptr to matrix values
: size(n), pData(pMem) {} 

void setDataPtr(T *ptr) { pData = ptr; }   // reassign pData
private:
std::size_t size;           // size of matrix
T *pData;       // pointer to matrix values
};

// 这就让派生类来决定如何分配内存。一些实现会在SquareMatrix对象内部存储矩形数据：
template<typename T, std::size_t n>
class SquareMatrix: private SquareMatrixBase<T> {
public:
SquareMatrix():SquareMatrixBase<T>(n, data) {} // // send matrix size and data ptr to base class
private:
T data[n*n];
};

// 这种类型的对象没有必要做动态内存分配，但是对象本身可能会非常大。一个替换的方法是为每个矩形在堆上存放数据：

template<typename T, std::size_t n>
class SquareMatrix: private SquareMatrixBase<T> {
public:
SquareMatrix(): SquareMatrixBase<T>(n, 0), // // set base class data ptr to null,allocate memory for matrix
pData(new T[n*n]) // values, save a ptr to the memory, and give a copy of it to the base class
{ this->setDataPtr(pData.get()); } 
private:
boost::scoped_array<T> pData;          // see Item 13 for info on
};                                   // boost::scoped_array

// 2.2.3 消除代码膨胀前后效率对比
// 不管将数据存放在哪里，从代码膨胀的角度来说，关键结果是现在很多（可能是所有的）SquareMatrix的成员函数可以简单的inline调用基类的（non-inline）函数版本，所有持有相同类型数据的矩形共享基类中的函数，不管size是多少。
// 同时，不同size的SquareMatrix对象属于不同类型，所以即使SquareMatrix<double,5>和SquareMatrix<double,10>对象在SquareMatrixBase<double>中使用相同的成员函数，
// 把一个SquareMatrix<double,5>对象传给一个需要SquareMatrix<double,10>的函数是没有机会的。好还是不好呢。

// 好是好，但是需要付出代价。矩形size大小固定的invert版本比按函数参数传递size大小（或者存储在对象中）的invert版本可能产生更好的代码。例如，在指定size的版本中，sizes是编译期常量，
// 因此是常量传播优化的合格者，也可以把其放入生成指令中作为直接操作数。这在同size无关的版本中无法做到。

// 从另外一个方面，为不同size的矩阵只提供一个invert版本可以减小可执行程序的大小，这能减少程序的工作集大小，并且能够强化指令高速缓存的引用集中化。这些东西能够使得程序运行速度更快，
// 并且相对size指定的版本才能做出的优化，它可能会做出更好的补偿。哪种方法效果更好？唯一的方法是两种方法都试一下，在你的特定平台和有代表性的数据集上观察它们的行为。

// 另外一个有关效率的需要考虑的地方是有关对象的大小。如果你不介意，将size大小无关的版本向上移动到基类中会增加每个对象的大小。例如，在我刚刚展示的代码中，每个SquareMatrix对象有一个指向SquareMatrixBase类中数据的指针。
// 即使每个派生类中已经有取得数据的方法，这也为每个SquareMatrix对象至少增加一个指针的大小。我们可以修改设计来去掉指针，但是这也是需要付出代价的。例如，让基类存储一个指向数据的protected指针，但会导致封装性的降低（Item 22）
// 它同样能导致资源管理并发症：如果基类存储了指向矩阵数据的指针，但是数据既有可能是动态分配的也可能存储在派生类对象中（正如我们看到的），如何决定是不是需要delete指针？这样的问题是有答案的，但是你做的越精细事情就变得越复杂。
// 从某种意义上讲，有一点代码重复开始开起来有点幸运了。

// 2.3 如何处理类型模板参数导致的代码膨胀
// 这个条款仅仅讨论了由于非类型模板参数导致的代码膨胀，但是类型参数同样可以导致代码膨胀。例如，在许多平台中，int和long有着相同的二进制表示，所以在成员函数中使用vector<int>和vector<long>看起来会一样，这正是代码膨胀的定义。
// 一些连接器会把相同的代码实现整合到一起，但是有一些不会，这就意味着由模板实例化的int和long版本会在一些环境中导致代码膨胀。类似的，在大多数平台上，所有的指针类型有着相同的二进制表示，
// 所以带指针类型的模板（例如，list<int*>,list<const*>,list<SquareMatrix<long,3>*>等等）应该通常能够为每个成员函数使用一个单一的底层实现。特别的，这就意味着实现一个强类型指针（T* 指针）的成员函数时，
// 让它们调用一个无类型指针的函数（void*指针）。一些标准C++库的实现为模板就是这么做的（如vector,deque,和list）。如果你关心在你的模板中出现的代码膨胀问题，你可能就会想开发出做相同事情的模板。

// 回到顶部
// 3. 总结
// 模板会产生多个类和多个函数，所以任何模板不应该依赖于会导致代码膨胀的模板参数。
// 非类型模板参数导致的代码膨胀通常情况下可以将模板参数替换为函数参数或者类数据成员来清除。
// 由类型参数导致的代码膨胀也可以被降低，方式是为实例化类型共享相同的二进制表示。



////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 45 使用成员函数模板来接受“所有兼容类型”
// 提到智能指针可用来自动释放堆中的内存，STL中的迭代器也是一种智能指针，它甚至支持链表元素指针的 ++操作。 这些高级特性是普通指针所没有的。本文以智能指针为例，介绍成员函数模板的使用。
// 隐式类型转换
// 智能指针虽然比普通指针提供了更多有用的特性，但也存在一些问题，比如我们有一个类的层级：
class Top{};
class Middle: public Top{};
class Bottom: public Middle{};
// 普通指针可以做到派生类指针隐式转换为基类指针：
Top *p1 = new Bottom;
const Top *p2 = p1;
// 但如果是智能指针，比如我们实现了SmartPtr，我们则需要让下面代码经过编译:
SmartPtr<Top> p1 = SmartPtr<Bottom>(new Bottom);
SmartPtr<const Top> p2 = p1;
// 同一模板的不同实例之间是没有继承关系的，在编译器看来 SmartPtr<Top> 和 SmartPtr<Bottom> 是完全不同的两个类。 所以上述代码直接编译是有问题的。
?为了支持用 SmartPtr<Bottom> 初始化 SmartPtr<Top> ，我们需要重载 SmartPtr 的构造函数。 原则上讲，有多少类的层级我们就需要写多少个重载函数。因为类的层级是会扩展的，因此需要重载的函数数目是无穷的。 这时便可以引入成员函数模板了:

template<typename T>
class SmartPtr{
public:
    template<typename U>
    SmartPtr(const SmartPtr<U>& other);
};
// 注意该构造函数没有声明为 explicit，是为了与普通指针拥有同样的使用风格。子类的普通指针可以通过隐式类型转换变成基类指针。
// 兼容类型检查
// 接受同一模板的其他实例的构造函数被称为通用构造函数。
// 事实上，通用构造函数提供了更多的功能。他可以把一个 SmartPtr<Top> 隐式转换为 SmartPtr<Bottom>，把一个 SmartPtr<int> 转换为 SmartPtr<double>。 但普通指针是不允许这些隐式转换的。
// 因此我们需要把它们禁用掉。注意一下通用构造函数的实现方式便可以解决这个问题：

template<typename T>
class SmartPtr{
public:
    template<typename U>
    SmartPtr(const SmartPtr<U>& other): ptr(other.get()){};
    T* get() const{ return ptr; }
private:
    T *ptr;
};
// 在 ptr(other.get()) 时编译器会进行类型的兼容性检查，只有当 U 可以隐式转换为 T 时，SmartPtr<U> 才可以隐式转换为 SmartPtr<T>。 这样就避免了不兼容指针的隐式转换。
// 其他使用方式
// 除了隐式类型转换，成员函数模板还有别的用途，例如赋值运算符。下面是 shared_ptr 的部分源码：

template<class T> 
class shared_ptr{
public:
    template<class Y>
        explicit shared_ptr(Y *p);
    template<class Y>
        shared_ptr<shared_ptr<Y> const& r>;
    template<class Y>
        shared_ptr& operator=(shared_ptr<Y> const& r);
};
// 可以看到普通指针 Y* 到 shared_ptr<Y> 声明了 explicit，需要显式的类型转换；而 shared_ptr<Y> 之间只需要隐式转换。
// 使用拷贝构造函数模板存在一个问题：编译器是会生成默认的拷贝构造函数？还是会从你的模板实例化一个拷贝构造函数？ 即 Y == T 场景下的编译器行为。
// 事实上，成员函数模板不会改变C++的规则。C++规则讲：如果你没有声明拷贝构造函数，那么编译器应该生成一个。 所以Y == T时拷贝构造函数不会从成员函数模板实例化，而是会自己生成一个。
所以 shared_ptr 模板中还是手动声明了拷贝构造函数：

template<class T>
class shared_ptr{
public:
    shared_ptr(shared_ptr const& r);
    template<class Y>
        shared_ptr(shared_ptr<Y> const& r);
 
    shared_ptr& operator=(shared_ptr const& r);
    template<class Y>
        shared_ptr& operator=(shared_ptr<Y> const& r);
};

// 总结
// 使用成员函数模板可以生成接受所有兼容类型的函数。
// 如果你为泛型化拷贝构造或泛型化赋值声明了成员模板，你依然需要声明常规拷贝构造函数和拷贝赋值运算符。








// 智能指针的行为像是指针，但是没有提供加的功能。例如，Item 13中解释了如何使用标准auto_ptr和tr1::shared_ptr指针在正确的时间自动删除堆上的资源。STL容器中的迭代器基本上都是智能指针,
// 当然，你不能通过使用“++”来将链表中的指向一个节点的内建指针移到下一个节点上去，但是list::iterator可以这么做。
// 1. 问题分析——如何实现智能指针的隐式转换
// 真正的指针能够做好的一件事情是支持隐式转换。派生类指针可以隐式转换为基类指针，指向非const的指针可以隐式转换成为指向const对象的指针，等等。例如，考虑可以在一个三层继承体系中发生的转换：

class Top {};
class Middle: public Top {};
class Bottom: public Middle {};
Top *pt1 = new Middle;           // convert Middle* ⇒ Top*
Top *pt2 = new Bottom;          // convert Bottom* ⇒ Top*
const Top *pct2 = pt1;             // convert Top* ⇒ const Top*

// 在用户自定义的智能指针中模仿这种转换是很微妙的。我们想让下面的代码通过编译：
template<typename T>
class SmartPtr {
public: // smart pointers are typically
explicit SmartPtr(T *realPtr); // initialized by built-in pointers
};

SmartPtr<Top> pt1 = SmartPtr<Middle>(new Middle); // convert SmartPtr<Middle> ⇒ SmartPtr<Top>
SmartPtr<Top> pt2 = SmartPtr<Bottom>(new Bottom); // convert SmartPtr<Bottom> ⇒ SmartPtr<Top>
SmartPtr<const Top> pct2 = pt1; // convert SmartPtr<Top> ⇒ SmartPtr<const Top>

// 同一个模板的不同实例之间没有固有的关系，所以编译器将SmartPtr<Middle>和SmartPtr<Top>视为完全不同的类，它们之间的关系不比vector<float>和Widget来的近。为了实现SmartPtr类之间的转换，我们必须显示的实现。
// 在上面的智能指针示例代码中，每个语句都创建了一个新的智能指针对象，所以现在我们把焦点放在如何实现出一个行为表现如我们所愿的智能指针构造函数。关键的一点是没有办法实现我们需要的所有构造函数。
// 在上面的继承体系中，我们可以用一个SmartPtr<Middle>或一个SmartPtr<Bottom>来构造一个SmartPtr<Top>，但是如果这个继承体系在未来扩展了，SmartPtr<Top>对象必须能够从其他智能指针类型中构造出来。例如，如果我们增加了下面的类：

class BelowBottom: public Bottom {};
// 我们将会需要支持用SmartPtr<BelowBottom>对象来创建SmartPtr<Top>对象，我们当然不想通过修改SmartPtr模板来实现它。

// 2. 使用成员函数模板——泛化拷贝构造函数进行隐式转换
// 从原则上来说，我们所需要的构造函数的数量是没有限制的。既然模板可以被实例化成为没有限制数量的函数，因此看上去我们不需要一个SmartPtr的构造函数，我们需要的是一个构造函数模板。
// 这样的模板是成员函数模板(member function templates) （也被叫做member templates）的一个例子——也即是为类产生成员函数的模板：

template<typename T>
class SmartPtr {
public:
template<typename U> // member template
SmartPtr(const SmartPtr<U>& other); // for a ”generalized copy constructor”
};
// 这就是说对于每个类型T和每个类型U，一个SmartPtr<T>能够用SmartPtr<U>创造出来，因为SmartPtr<T>有一个以SmartPtr<U>作为参数的构造函数 
// 像这样的构造函数——用一个对象来创建另外一个对象，两个对象来自于相同的模板但是它们为不同类型（例如，用SmartPtr<U>来创建SmartPtr<T>）,它通常被叫做泛化拷贝构造函数（generalized copy constructors）

// 2.1 隐式转换不需要explicit
// 上面的泛化拷贝构造函数并没有被声明为explicit。这是经过仔细考虑的。内建指针类型之间的类型转换（例如从派生类转换到基类指针）是隐式的，并且不需要cast，因此智能指针模仿这种行为就是合理的。在模板化的构造函数上省略explicit正好做到了这一点。

// 2.2 将不符合要求的模板实例化函数剔除掉
// 为SmartPtr实现的泛化拷贝构造函数比我们想要的提供了更多的东西。我们想要用SmartPtr<Bottom>创建SmartPtr<Top>，但是我们不想用SmartPtr<Top>创建SmartPtr<Bottom>，
// 因为这违背了public继承的含义（Item 32） 我们同样不想用SmartPtr<double>创建SmartPtr<int>,因为没有从double*到int*之间的隐式转换。因此，我们必须将成员模板生成的这种成员函数集合剔除掉。
// 假设SmartPtr遵循auto_ptr和tr1::shared_ptr的设计，也提供一个get成员函数来返回智能指针对象所包含的内建类型指针的一份拷贝（Item 15），我们可以使用构造函数模板的实现来对一些转换进行限制：

template<typename T>
class SmartPtr {
public:
template<typename U>
SmartPtr(const SmartPtr<U>& other): heldPtr(other.get()) { ... } // initialize this held ptr with other’s held ptr
T* get() const { return heldPtr; }
private: 
T *heldPtr; // built-in pointer held by the SmartPtr
}

// 我们在成员初始化列表中用SmartPtr<U>中包含的类型为U*的指针来初始化SmartPtr<T>中的类型为T*的数据成员。这只有在能够从U*指针到T*指针进行隐式转换的情况下才能通过编译，这也正是我们所需要的。
// 实际结果是现在SmartPtr<T>有了一个泛化拷贝构造函数，只有传递的参数为兼容类型时才能够通过编译。

3. 成员函数模板对赋值的支持
成员函数模板的使用不仅仅限定在构造函数上。它们的另外一个普通的角色是对赋值的支持。例如，tr1的shared_ptr（Item 13）支持用所有兼容的内建指针来对其进行构造，可以用tr1::shared_ptr，auto_ptr和tr1::weak_ptr（Item 54）来进行构造，对赋值也同样使用，但是tr1::weak_ptr例外。下面是从tr1的说明中摘录下来的tr1::shared_ptr的实现，可以看到在声明模板参数的时候它倾向于使用class而不是typename。（Item 42中描述的，在这个上下文中它们的意义相同。）


template<class T> class shared_ptr {
public:

template<class Y>                              // construct from

explicit shared_ptr(Y * p);                   // any compatible

template<class Y>                              // built-in pointer,


shared_ptr(shared_ptr<Y> const& r); // shared_ptr,
template<class Y> // weak_ptr, or

explicit shared_ptr(weak_ptr<Y> const& r);    // auto_ptr

template<class Y>

explicit shared_ptr(auto_ptr<Y>& r);

template<class Y> // assign from
shared_ptr& operator=(shared_ptr<Y> const& r); // any compatible
template<class Y> // shared_ptr or
shared_ptr& operator=(auto_ptr<Y>& r); // auto_ptr
...
};

所有的这些构造函数都是explicit的，除了泛化拷贝构造函数。这就意味着从shared_ptr的一种类型隐式转换到shared_ptr的另一种类型是允许的，但是内建类型指针和其他的智能指针类型到shared_ptr的隐式转换是禁止的。（显示的转换是可以的（例如通过使用cast））。同样有趣的是传递给tr1::shared_ptr构造函数和赋值运算符的auto_ptr没有被声明为const，但是tr1::shared_ptr和tr1::weak_ptr的传递却声明为const了。这是因为auto_ptr被拷贝的时候已经被修改了（Item 13）。

 

回到顶部
4. 成员函数模板会生成默认拷贝构造函数
 

成员函数模板是美好的东西，但是它们没有修改语言的基本规则。Item 5解释了编译器会自动生成的4个成员函数中的两个函数为拷贝构造函数和拷贝赋值运算符。Tr1::shared_ptr声明了一个泛化拷贝构造函数，很清楚的是如果类型T和类型Y是相同的，泛化拷贝构造函数就会被实例化成一个“普通”的拷贝构造函数。那么编译器会为tr1::shared_ptr生成一个拷贝构造函数么？或者说用相同类型的tr1::shared_ptr构造另外一个tr1::shared_ptr的时候，编译器会实例化泛化拷贝构造函数么？

 

正如我所说的，成员模板没有修改语言的规则。“如果你需要一个拷贝构造函数而你没有自己声明，编译器会为你生成一个”这条规则也是其中之一。在一个类中声明一个泛化拷贝构造函数（一个member template）不会阻止编译器生成它们自己的拷贝构造函数（non-template），所以如果你想控制拷贝构造函数的所有方面，你必须同时声明一个泛化拷贝构造函数和“普通的”构造函数。对于赋值同样适用。下面是tr1::shared_ptr的定义：

template<class T> class shared_ptr {
public:
shared_ptr(shared_ptr const& r);       // copy constructor

template<class Y>                        // generalized



shared_ptr(shared_ptr<Y> const& r);       // copy constructor

shared_ptr& operator=(shared_ptr const& r);            // copy assignment

template<class Y>                                     // generalized


shared_ptr& operator=(shared_ptr<Y> const& r); // copy assignment
...
};

5. 总结
使用成员函数模板来生成接受所有兼容类型的函数。
如果你为泛化拷贝构造函数和泛化赋值运算符声明成员模板，你同样需要声明普通的拷贝构造函数和拷贝赋值运算符。







//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 Item 46 如果想进行类型转换，在模板内部定义非成员函数

1. 问题的引入——将operator*模板化
Item 24中解释了为什么对于所有参数的隐式类型转换，只有非成员函数是合格的，并且使用了一个为Rational 类创建的operator*函数作为实例。在继续之前建议你先回顾一下这个例子，因为这个条款的讨论是对它的扩展，我们会对Item 24的实例做一些看上去无伤大雅的修改：对Rational和opeartor*同时进行模板化：

template<typename T>
class Rational {
public:
Rational(const T& numerator = 0, // see Item 20 for why params

const T& denominator = 1);         // are now passed by reference

const T numerator() const;          // see Item 28 for why return


const T denominator() const; // values are still passed by value,
... // Item 3 for why they’re const
};
template<typename T>
const Rational<T> operator*(const Rational<T>& lhs,
const Rational<T>& rhs)
{ ... }


正如Item 24中讨论的，我们想支持混合模式的算术运算，所以我们想让下面的代码通过编译。这应该没有问题，因为我们在Item 24中使用了相同的代码。唯一的区别是Rational和operator*现在变成了模板：

1 Rational<int> oneHalf(1, 2);            // this example is from Item 24,
2 // except Rational is now a template
3 
4 Rational<int> result = oneHalf * 2; // error! won’t compile
 

回到顶部
2. 问题分析——模板参数演绎不能进行隐式转换

但事实上上面的代码不会通过编译，这就表明了模板化的Rational和非模板版本有些地方还是不一样的，确实是有区别的。在Item 24中，编译器知道我们尝试调用什么函数（带两个Rational参数的operator*），但是这里，编译器不知道我们想要调用哪个函数。相反，它们尝试确认从模板operator*中实例化出（也即是创建）什么样的函数。它们知道它们想实例化一些名字为operator*的函数，这些函数带有两个类型为Rational<T>的参数，但是为了进行实例化，它们必须确认T是什么。问题是他们不可能知道。

为了演绎出T类型，它们看到了调用operator*时传递的参数类型。在上面的例子中，两个参数类型分别是Rational<int>（oneHalf的类型）和int(2的类型)。每个参数进行单独考虑。

使用oneHalf进行演绎（deduction）很容易，operator*的第一个参数所需要的类型为Rational<T>，实际上这里传递给operator*的第一个参数的类型是Rational<int>，所以T必须为int。不幸的是，对其他参数的演绎就没有这么简单了，operator*的第二个参数所需要的类型也为Rational<T>，但是传递给operator*的第二个参数是一个int值。在这种情况下编译器该如何确认T是什么呢？你可能期望它们使用Rational<int>的非显示构造函数来将2转换为一个Rational<int>，这样就允许它们将T演绎成int，但是它们没有这么做。因为在模板参数演绎期间永远不会考虑使用隐式类型转换函数。这样的转换是在函数调用期间被使用的，所以在你调用一个函数之前，你必须知道哪个函数是存在的。为了知道这些，你就必须为相关的函数模板演绎出参数类型（然后你才能实例化出合适的函数。）但是在模板参数演绎期间不会通过调用构造函数来进行隐式转换。Item 24没有涉及到模板，所以模板参数的演绎不是问题。现在我们正在讨论C++的模板部分（Item 1）,这变为了主要问题。

 

回到顶部
3. 问题解决——使用友元函数
3.1 在类模板中声明友元函数——编译通过
 

我们可以利用如下事实来缓和编译器接受的对模板参数演绎的挑战：模板类中的一个友元声明能够引用一个实例化函数。这就意味着类Ration<T>能够为Ration<T>声明一个友元函数的operator*。类模板不再依赖于模板参数演绎（这个过程只应用于函数模板），所以T总是在类Ration<T>被实例化的时候就能被确认。所以声明一个合适的友元operator*函数能简化整个问题：

template<typename T>
class Rational {
public:
...

friend // declare operator*
const Rational operator*(const Rational& lhs, // function (see
const Rational& rhs); // below for details)
};

template<typename T>                                                              // define operator*

const Rational<T> operator*(const Rational<T>& lhs, // functions


const Rational<T>& rhs)
{ ... }


现在我们对operator*的混合模式的调用就能通过编译了，因为当对象oneHalf被声明为类型Rational<int>的时候，Ratinonal<T>被实例化称Rational<int>,作为这个过程的一部分，参数为Rational<int>的友元函数operator*被自动声明。作为一个被声明的函数（不是函数模板），编译器在调用它时就能够使用隐式类型转换函数（像Rational的非显示构造函数），这就是如何使混合模式调用成功的。

3.2 关于模板和模板参数的速写符号
虽然代码能够通过编译，但是却不能链接成功。我们稍后处理，但是对于上面的语法我首先要讨论的是在Rational中声明operator*。

在一个类模板中，模板的名字能够被用来当作模板和模板参数的速写符号，所以在Rational<T>中，我们可以写成Rational来代替Rational<T>。在这个例子中只为我们的输入减少了几个字符，但是如果有多个参数或者更长的参数名字的时候，它既能减少输入也能使代码看起来更清晰。我提出这些是因为在上面的例子中operator*的声明用Rational作为参数和返回值，而不是Rational<T>。下面的声明效果是一样的：



template<typename T>
class Rational {
public:
...
friend
const Rational<T> operator*(const Rational<T>& lhs,
const Rational<T>& rhs);
...
};

然而，使用速写形式更加容易（更加大众化）。

3.3 把友元函数的定义合并到声明中——链接通过
现在让我们回到链接问题。混合模式的代码能够通过编译，因为编译器知道我们想调用一个实例化函数（带两个Rational<int>参数的operator*函数），但是这个函数只在Rational内部进行声明，而不是被定义。我们的意图是让类外部的operator*模板提供定义，但是编译器不会以这种方式进行工作。如果我们自己声明一个函数（这是我们在Rational模板内部所做的），我们同样有责任去定义这个函数。在上面的例子中，我们并没有提供一个定义，这就是为什么连接器不能知道函数定义的原因。

最简单的可能工作的解决方案是将operator*函数体合并到声明中：

template<typename T>
class Rational {
public:
...
friend const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.numerator() * rhs.numerator(), // same impl
lhs.denominator() * rhs.denominator()); // as in
} // Item 24
};

确实这能够工作：对operator*的混合模式调用，编译，链接，运行都没有问题。

3.4 如此使用友元函数很意思
这种技术的有意思的地方是友元函数并没有被用来访问类的非public部分。为了使所有参数间的类型转换成为可能，我们需要一个非成员函数（Item 24在这里仍然适用）；并且为了让合适的函数被自动实例化出来，我们需要在类内部声明一个函数。在类内部声明一个非成员函数的唯一方法是将其声明为友元函数。这就是我们所做的，不符合惯例？是的。有效么？毋庸置疑。

回到顶部
4. 关于模板友元函数inline的讨论
正如在Item 30中解释的，在类内部定义的函数被隐式的声明为inline函数，这同样包含像operator*这样的友元函数。你可以最小化这种inline声明的影响：通过让operator*只调用一个定义在类体外的helper函数。在这个条款的例子中没有必要这么做，因为operator*已经被实现成了只有一行的函数，对于更加复杂的函数体，helper才可能是你想要的。“让友元函数调用helper”的方法值得一看。

Rationl是模板的事实意味着helper函数通常情况下也会是一个模板，所以在头文件中定义Rational的代码会像下面这个样子：

template<typename T> class Rational; // declare
// Rational
// template

template<typename T> // declare
const Rational<T> doMultiply( const Rational<T>& lhs, // helper

const Rational<T>& rhs);                                         // template

template<typename T>
class Rational {
public:
...
friend
const Rational<T> operator*(const Rational<T>& lhs,
const Rational<T>& rhs)                                          // Have friend



{ return doMultiply(lhs, rhs); }                // call helper

...

};

许多编译器从根本上强制你将所有的模板定义放在头文件中，所以你可能同样需要在你的头文件中定义doMultiply。（正如Item30解释的，这样的模板不需要inline）。这会像下面这个样子：

template<typename T> // define
const Rational<T> doMultiply(const Rational<T>& lhs, // helper
const Rational<T>& rhs) // template in
{ // header file,
return Rational<T>(lhs.numerator() * rhs.numerator(), // if necessary
lhs.denominator() * rhs.denominator());
}

当然，作为一个模板，doMultiply不支持混合模式的乘法，但是也不需要支持。它只被operator*调用，operator*支持混合模式操作就够了！从根本上来说，函数operator*支持必要的类型转换，以确保两个Rational对象被相乘，然后它将这两个对象传递到doMultiply模板的合适实例中进行实际的乘法操作。协同行动，不是么？

回到顶部
5. 总结
当实现一个提供函数的类模版时，如果这些函数的所有参数支持和模板相关的隐式类型转换，将这些函数定义为类模板内部的友元函数。





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 47 使用traits class表示类型信息

STL主要由为容器，迭代器和算法创建的模板组成，但是也有一些功能模板。其中之一叫做advance。Advance将一个指定的迭代器移动指定的距离：

1 template<typename IterT, typename DistT> // move iter d units
2 void advance(IterT& iter, DistT d); // forward; if d < 0,
3 // move iter backward
 

从概念上来说，advance仅仅做了iter += d，但是advance并不是用这种方式实现的，因为只有随机访问迭代器支持+=操作。其他一些更加弱的迭代器类型必须通过反复的++或者--d次来实现advance。

回到顶部
1. 五种迭代器种类回顾
你不记得STL迭代器的种类了？没问题，我们现在做一些简单的回顾。总共有5类迭代器，对应着它们支持的五种操作。输入迭代器（input iterator）只能向前移动，一次只能移动一步，只能读取它们指向的内容，并且只能读一次。这种迭代器模拟的是输入文件的读指针；C++库的istream_iterator是这种迭代器的代表。输出迭代器（output iterator）同输入迭代器类似，但是对于输出迭代器来说：它们只能向前移动，每次只能移动一步，只能对它们指向的内存进行写操作，并且只能写一次。它们模拟的是输出文件的写指针；ostream_iterator代表了这种迭代器。这是两类功能最弱的迭代器。因为输入和输出迭代器只能向前移动，只能对它们所指向的内容进行读写最多一次，它们只能为one-pass算法所使用。

一个更加强大的迭代器种类由前向迭代器（forward iterator）组成。这种迭代器能做输入和输出迭代器能做的所有事情，并且它们能对它们指向的内容进行多次的读写。这就使得它们能被multi-pass算法所使用。STL没有提供单链表，但是一些库却提供了（通常叫做slist），这种容器中的迭代器为前向迭代器。TR1中的哈希容器（Item 54）迭代器也可能是前向迭代器。

双向迭代器（bidirectional iterators）和前向迭代器相比添加了向后移动的能力。为STL中的list提供的迭代器就属于这种类别，为set,multiset，map和multimap提供的迭代器也是这种类别。

最强大的迭代器类别叫做随机访问迭代器（random access iterator）。这种类型的迭代器和双向迭代器相比添加了执行“迭代器运算（iterator arithmetic）”的能力，也就是在常量时间内向前或者向后跳跃任意的距离。这种运算同指针运算类似，不要吃惊，因为随机访问迭代器模拟的就是内建类型的指针，内建类型指针的行为表现就如同随机访问迭代器。Vector,deque和string迭代器都是随机访问迭代器。

为了识别这五种迭代器类型，C++在标准库中为五种迭代器类型提供了一个“tag结构体”：

1 struct input_iterator_tag {};
2 struct output_iterator_tag {};
3 struct forward_iterator_tag: public input_iterator_tag {};
4 struct bidirectional_iterator_tag: public forward_iterator_tag {};
5 struct random_access_iterator_tag: public bidirectional_iterator_tag {};
 

这些结构体之间的继承关系是有效的“is-a”关系（Item32）：所有的前向迭代器同样是输入迭代器，等等。我们很快会看到这种继承的效用。

回到顶部
2. 如何实现advance简析
回到advance。考虑到不同的迭代器功能，实现advance的一种方法是使用循环的最小公分母策略：对迭代器进行反复加或者减。然而，这个方法会花费线性的时间。随机访问迭代器支持常量时间的迭代器算法，在我们需要的时候会使用它的这种能力。

我们真正想要的是像下面这样去实现advance:

template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if (iter is a random access iterator) {

iter += d;                           // use iterator arithmetic

}                                        // for random access iters

else {


if (d >= 0) { while (d--) ++iter; } // use iterative calls to
else { while (d++) --iter; } // ++ or -- for other
} // iterator categories
}


这需要决定iter是不是一个随机访问迭代器，也就是需要知道它的类型，IterT，是不是随机访问迭代器类型。换句话说，我们需要获取一个类型的相关信息。这也是trait让你所做的：它们允许你在编译期间获取一个类型的相关信息。

回到顶部
3. Traits技术分析
3.1 使用traits技术的要求
Traits不是C++中的关键字或者一个预定义的概念；它们是一种技术，也是一个C++程序员需要遵守的约定。使用这项技术的一个要求是它必须使内建类型同用户自定义类型一样能够很好的工作。例如，如果advance的入参为一个指针（像const char*）和一个Int,advance必须能够工作，但是这就意味着trait技术必须能够使用在像指针一样的内建类型上。

Traits必须能够同内建类型一块工作就意味着不能在类型内部嵌入一些信息，因为没有方法在指针内部嵌入信息。于是对于一种类型的traits信息，必须是放在类型外部的。标准的技术是将其放在模板和模板的一个或多个特化实例中。对于迭代器来说，标准库中的模板被命名为iterator_traits:

1 template<typename IterT> // template for information about
2 struct iterator_traits; // iterator types
 

正如你所见的，iterator_traits是一个结构体。按照惯例，traits经常被实现为一个结构体。另外一种常用手法是将实现traits的结构体替换为traits class(这不是我说的)。

Iterator_traits的工作方式是对于每个类型IterT，在结构体iterator_traits<IterT>中声明一个叫做iterator_category的typedef。这个typedef唯一确认了IterT的迭代器类别。

3.2 实现traits class需要处理用户自定义类型
Iterator_traits会在两部分中实现它。首先，它强制任何用户自定义的迭代器类型必须包含一个叫做iterator_category的内嵌typedef，它能够识别合适的tag结构体。举个例子，deque的迭代器是随机访问的，因此一个deque迭代器的类会像是下面这个样子：

template < ... >                                                                         // template params elided

class deque {

public:

class iterator {

public:

typedef random_access_iterator_tag iterator_category;

...

};

...

};

List的迭代器是双向的，所以用下面的方式处理：
template < ... >

class list {

public:

class iterator {

public:

typedef bidirectional_iterator_tag iterator_category;

...

};

...

};

 iterator_traits只是重复使用iterator类的内嵌typedef:

 // the iterator_category for type IterT is whatever IterT says it is;

// see Item 42 for info on the use of “typedef typename”

template<typename IterT>

struct iterator_traits {

typedef typename IterT::iterator_category iterator_category;

...

};


3.3 实现traits class需要处理指针类型
这对用户自定义类型来说会工作的很好，但是对于指针迭代器来说就不工作了，因为指针中没有内嵌的typedef。Iterator_trait实现的第二部分需要处理指针迭代器。

为了支持这种迭代器，iterator_traits为指针类型提供了一种部分模板特化（partial template specialization）。指针的行为表现同随机访问迭代器类似，所以iterator_trait为它们指定了随机访问类别：

template<typename T> // partial template specialization
struct iterator_traits<T*> // for built-in pointer types
{
typedef random_access_iterator_tag iterator_category;
...
};

3.4 实现traits class总结
到现在你应该了解了如何设计和实现一个traits class：

确认你想要支持的类型的一些信息(例如，对于迭代器来说，它们的迭代器类别)。
为了确认信息，你需要选择一个名字（例如，iterator_category）
为你想支持的类型提供包含相关信息的一个模板和一些特化（例如，iterator_traits）
回到顶部
4. 使用traits class实现advance
4.1 类别判断不应该在运行时进行
考虑iterator_traits——实际上是std::iterator_traits，既然它是C++标准库的一部分——我们能为advance的实现精炼成我们自己的伪代码：


template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if (typeid(typename std::iterator_traits<IterT>::iterator_category) ==
typeid(std::random_access_iterator_tag))
...
}


虽然这看上去很有希望，但它不会如我们所愿。第一，它会导致编译问题，这个问题我们将在Item 48研究；现在，有更加基本的问题需要考虑。IterT的类型在编译期被确认，所以iterator_traits<IterT>::iterator_category同样能够在编译期被确定。但是if语句会在运行时被评估（除非你的优化器足够疯狂，把if语句去掉）。为什么将本来可以在编译期做的事情挪到运行时去做呢？它会浪费时间，并且会造成执行代码膨胀。

4.2 将条件评估提前到编译期——使用重载
我们真正想要的是为类型提供一个能够在编译期进行评估的条件结构（也就是一个if…else语句）。C++已经有一种方法来实现这种行为。她叫做重载。

当你重载某个函数f的时候，你为不同的重载函数指定不同的参数类型。当你调用f时，编译器会根据你所传递的参数选择最佳匹配重载函数。编译器会说：“如果这个重载对于传递过来的参数来说是最佳匹配，那么调用这个f；如果另外一个重载函数是最佳匹配，那么调用另外一个函数；如果第三个函数是最佳匹配，调用第三个”等等，看到了么？这是一个与类型相关的编译期条件结构。为了让advance表现出我们想要的行为，所有我们必须要做的是创建一个重载函数的多个版本，它们包含了advance的“内脏”，每个函数都带有一个不同类型的iterator_category对象。我将这些函数命名为doAdvance：


template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // random access
std::random_access_iterator_tag) // iterators
{
iter += d;
}
template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // bidirectional
std::bidirectional_iterator_tag) // iterators
{
if (d >= 0) { while (d--) ++iter; }
else { while (d++) --iter; }
}
template<typename IterT, typename DistT> // use this impl for
void doAdvance(IterT& iter, DistT d, // input iterators
std::input_iterator_tag)
{
if (d < 0 ) {
throw std::out_of_range("Negative distance"); // see below
}
while (d--) ++iter;
}

因为forward_iterator_tag继承自input_iterator_tag，为input_iterator_tag提供的doAdvance版本同样能够处理forward迭代器。这是在不同iterator_tag结构体之间引入继承的动机。（事实上，这也是所有的使用public继承的动机：为基类类型实现的代码对于派生类类型来说同样适用。）

对于随机访问迭代器和双向迭代器来说，advance的特化版本同时能够做正向或者负向的移动，但是对于forward迭代器或者input迭代器来说，如果你想进行一个负向的移动就会出现未定义行为。实现中如果简单的假设d是非负的，当传递一个负参数时，你就会进入一个很长的循环中，直到d变为0为止。在上面的代码中，我所使用的替代方法是抛出一个异常。两种实现都是有效的。这就是未定义行为的诅咒：你不能预测出来会发成什么。

考虑为doAdvance所重载的不同版本，所有advance需要做的就是调用它们，传递一个额外的合适的迭代器类别对象，最后编译器就能够使用重载方案来调用合适的实现：


template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
doAdvance( // call the version
iter, d, // of doAdvance
typename // that is
std::iterator_traits<IterT>::iterator_category() // appropriate for
); // iter’s iterator

}                                                                                  // category


5. traits class使用总结
 

我们可以总结一下如何使用traits class:

创建一系列重载的”worker”函数或者函数模板（例如，doAdvance），通过使用traits 参数来进行区分。根据传递的traits信息来对应的实现每个函数。
创建一个“master”函数或者函数模板（例如，advance）来调用worker，将traits class提供的信息传递进去。
 

Traits被广泛使用在标准库中。对于iterator_traits来说，除了iterator_category，还为迭代器提供了四种其它的信息（最有用的就是value_type—Item 42中给出了一个例子。）还有char_traits，存储了字符类型的信息，numeric_limits,提供数字类型信息，例如，它们能够表示的最大和最小值等等。（numeric_limits这个名字可能让你感到意外，因为传统的命名方式是以“traits”结尾，但是numeric_limits没有遵守。）

 

TR1（Item 54）为了为类型提供信息引入了大量的新的traits class，包括is_fundamental<T>(判断T是否为内建类型)，is_array<T>(判断T是否为数组)，和is_base_of<T1,T2>（判断T1和T2是否相同或者是T2的基类）。TR1向标准C++中添加了大概有50个traits classes。

 

回到顶部
6. 本条款总结 
Traits classes使得在编译期就能够获得类型信息。它们用模板和模板特化版本来进行实现。
利用重载，traits classes使得在类型上执行编译期if-else测试成为可能。




//////////////////////////////////////////////////////////////////////////////////////////
//  Item 48 了解模板元编程

1. TMP是什么？
模板元编程（template metaprogramming TMP）是实现基于模板的C++程序的过程，它能够在编译期执行。你可以想一想：一个模板元程序是用C++实现的并且可以在C++编译器内部运行的一个程序，它的输出——从模板中实例化出来的C++源码片段——会像往常一样被编译。

回到顶部
2. 使用TMP的优势
如果这没有冲击到你，是因为你没有足够尽力去想。

 

C++不是为了模板元编程而设计的，但是自从TMP早在1990年被发现之后，它就被证明是非常有用的，为了使TMP的使用更加容易，在C++语言和标准库中加入了一些扩展。是的，TMP是被发现的，而不是被发明。当模板被添加到C++中的时候TMP这个特性就被引入了。对于某些人来说所有需要做的就是关注如何以一种聪明的和意想不到的方式来使用它。

TMP有两种强大的力量。第一，它使得一些事情变得容易，也即是说如果没有TMP，这些事情做起来很难或者不可能实现。第二，因为模板元编程在C++编译期执行，它们可以将一些工作从运行时移动到编译期。一个结果就是一些原来通常在运行时能够被发现的错误，现在在编译期就能够被发现了。另外一个结果就是使用TMP的C++程序在基本上每个方面都更加高效：更小的执行体，更短的运行时间，更少的内存需求。（然而，将工作从运行时移到编译期的一个后果就是编译时间增加了。使用TMP的程序比没有使用TMP的程序可能消耗更长的时间来进行编译。）

回到顶部
3. 如何使用TMP？
3.1 再次分析Item 47中的实例
考虑在Item 47中为STL的advance写出来的伪代码。我已经为伪代码部分做了粗体：


template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if (iter is a random access iterator) {

iter += d;                           // use iterator arithmetic

}                                        // for random access iters

else {


if (d >= 0) { while (d--) ++iter; } // use iterative calls to
else { while (d++) --iter; } // ++ or -- for other
} // iterator categories
}

我们可以使用typeid替换伪代码，让程序能够执行。这就产生了一个“普通的”C++方法——也就是所有工作都在运行时开展的方法：


template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if ( typeid(typename std::iterator_traits<IterT>::iterator_category) ==
typeid(std::random_access_iterator_tag)) {

iter += d;                           // use iterator arithmetic

}                                        // for random access iters

else {


if (d >= 0) { while (d--) ++iter; } // use iterative calls to
else { while (d++) --iter; } // ++ or -- for other
} // iterator categories
}

Item 47指出这种基于typeid的方法比使用trait效率更低，因为通过使用这种方法，（1）类型测试发生在运行时而不是编译期（2）执行运行时类型测试的代码在运行的时候必须可见。事实上，这个例子也展示出了为什么TMP比一个“普通的”C++程序更加高效，因为traits方式属于TMP。记住，trait使得在类型上进行编译期if…else运算成为可能。

我已经在前面提到过一些东西说明其在TMP中比在“普通”C++中更加容易，Item 47中也提供了一个advance的例子。Item 47中提到了advance的基于typeid的实现会导致编译问题，看下面的例子：

1 std::list<int>::iterator iter;
2 ...
3 advance(iter, 10);               // move iter 10 elements forward;
4 // won’t compile with above impl.
 

考虑为上面调用所产生的advance的版本，将模板参数IterT和DistT替换为iter和10的类型之后，我们得到下面的代码：

void advance(std::list<int>::iterator& iter, int d)
{
if (typeid(std::iterator_traits<std::list<int>::iterator>::iterator_category) ==
typeid(std::random_access_iterator_tag)) {

iter += d;

// error! won’t compile


}
else {
if (d >= 0) { while (d--) ++iter; }
else { while (d++) --iter; }
}
}

有问题的是高亮部分，就是使用+=的语句。在这个例子中，我们在list<int>::iterator上使用+=，但是list<int>::iterator是一个双向迭代器（见Item 47），所以它不支持+=。只有随机访问迭代器支持+=。现在，我们知道了+=这一行将永远不会被执行到，因为为list<int>::iteraotr执行的typeid测试永远都不会为真，但是编译器有责任确保所有的源码都是有效的，即使不被执行到，当iter不是随机访问迭代器“iter+=d”就是无效代码。将它同基于tratis的TMP解决方案进行比较，后者把为不同类型实现的代码分别放到了不同的函数中，每个函数中进行的操作只针对特定的类型。

3.2 TMP是图灵完全的
TMP已经被证明是图灵完全的（Turing-Complete），这也就意味着它足够强大到可以计算任何东西。使用TMP，你可以声明变量，执行循环，实现和调用函数等等。但是这些概念同“普通”C++相对应的部分看起来非常不同。例如，Item 47中if…else条件在TMP中是如何通过使用模板和模板特化来表现的。但这是程序级别（assembly-level）的TMP。TMP库（例如，Boost MPL，见Item 55）提供了更高级别的语法，这些语法不会让你误认为是“普通的”C++。

3.3 TMP中的循环通过递归来实现
再瞥一眼事情在TMP中是如何工作的，让我们看一下循环。TMP中没有真正的循环的概念，所以循环的效果是通过递归来完成的。（如果一提到递归你就不舒服，在进入TMP 冒险之前你就需要处理好它。TMP主要是一个函数式语言，递归对于函数式语言就如同电视对美国流行文化一样重要：它们是不可分割的。）即使是递归也不是普通的递归，因为TMP循环没有涉及到递归函数调用，所涉及到的是递归模板实例化（template instantiations）。

TMP的“hello world”程序是在编译期计算阶乘。它算不上是令人激动的程序，“hello world”也不是，但是这两个例子对于介绍语言都是有帮助的。TMP阶乘计算通过对模板实例进行递归来对循环进行示范。也同样示范了变量是如何在TMP中被创建和使用的，看下面的代码：

template<unsigned n>          // general case: the value of

struct Factorial {                   // Factorial<n> is n times the value


// of Factorial<n-1>
enum { value = n * Factorial<n-1>::value };
};
template<> // special case: the value of
struct Factorial<0> { // Factorial<0> is 1
enum { value = 1 };
};

考虑上面的模板元编程（真的仅仅是单一的元函数Factorial），你通过引用Factorial<n>::value来得到factorial(n)的值。

代码的循环部分发生在模板实例Factorial<n>引用模板实例Factorial<n-1>的时候。像所有递归一样，有一种特殊情况来让递归终止。在这里是模板特化Factorial<0>。

每个Factorial模板的实例都是一个结构体，每个结构体使用enum hack（Item 2）来声明一个叫做value的TMP变量。Value持有递归计算的当前值。如果TMP有一个真正的循环结构，value将会每次循环的时候进行更新。既然TMP使用递归模板实例来替换循环，每个实例会得到它自己的value的拷贝，每个拷贝都会有一个和“循环”中位置想对应的合适的值。

你可以像下面这样使用Facorial：


int main()
{
std::cout << Factorial<5>::value; // prints 120

std::cout << Factorial<10>::value;       // prints 3628800

}

如果你认为这比冰激凌更酷，你就已经获得模板元程序员需要的素材。如果模板和特化，递归实例和enum hacks,还有像Factorial<n-1>::value这样的输入使你毛骨悚然，你还是一个“普通的”C++程序员。

3.4 TMP还能够做什么？
当然，Factorial对TMP的功能进行了示范，如同“hello world”程序对任何传统编程语言的功能进行示范一样。为了让你明白为什么TMP是值得了解的，知道它能够做什么很重要，这里有三个例子：

确保因次单位（dimensional unit）的正确性。在科学和工程应用中，把因次单位（例如，质量，距离和时间）正确的拼到一起是很必要的。将表示质量的变量赋值给表示速度的变量是错误的，但是用距离变量除以时间变量然后将结果赋值被速度变量就没有问题。通过使用TMP，确保（在编译期间）程序中的所有因次单元组合的正确性就是可能的，不管计算有多复杂。（这也是使用TMP来侦测早期错误的一个例子。）TMP这种用法的一个有趣的方面是它能够支持分数因次的指数。这需要在编译期间将分数简化，然后编译器才能够确认，例如，单元 time1/2同time4/8是相同的。
优化矩阵操作。Item 21中解释了有一些函数（包括 operator*）必须返回新的对象，Item 44中引入了SquareMatrix类，考虑下面的代码：

1 typedef SquareMatrix<double, 10000> BigMatrix;
2 BigMatrix m1, m2, m3, m4, m5; // create matrices and
3 ... // give them values
4 BigMatrix result = m1 * m2 * m3 * m4 * m5; // compute their product
 用“普通的”方式来计算result会有四次创建临时matrice对象的调用，每次调用都应用在对operator*调用的返回值上面。这些独立的乘法在矩阵元素上产生了四          次循环。使用TMP的高级模板技术——表达式模板（expression templates），来消除临时对象以及合并循环是有可能的，并且不用修改上面的客户端代码的语法。最   后的程序使用了更少的内存，而且运行速度会有很大的提升。

产生个性化的设计模式实现。像策略模式，观察者模式，访问者模式等等这些设计模式能够以很多方式被实现。使用基于模板的技术被叫做policy-based设计，我们可以创建表示独立设计选择（choice或者叫”policies”）的 模板，这些模板可以以任意的方式进行组合来产生个性化的模式实现。例如，使用这种技术能够创建一些实现智能指针行为策略（policies）的模板,使用它能够产生（在编译期）上百种不同的智能指针类型。这项技术已经超越了编程工艺领域，如设计模式和智能指针，它成为了生殖编程（generative programming）的基础。
回到顶部
4. TMP现状分析
TMP并不是为每个人准备的。因为语法不直观，支持的相关工具也很弱。（像为模板元编程提供的调试器。）作为一个“突然性“的语言它只是最近才被发现的，TMP编程的一些约定正在实验阶段。然而通过将工作从运行时移到编译期所带来的效率提升带给人很深刻的印象，对一些行为表达的能力（很难或者不可能在运行时实现）也是很吸引人的。

对于TMP的支持正在上升期。很可能下个版本的C++就是显示的支持它。TR1中已经支持了（Item 54）。关于这个主题的书籍已经开始出来了，网上的一些关于TMP信息也越来越多。TMP可能永远不会成为主流，但是对于一些程序员来说——尤其是程序库的实现者——几乎必然会成为主要手段。

回到顶部
5. 总结
模板元编程可以将工作从运行时移到编译期，这样可以更早的发现错误，并且提高运行时性能。
基于策略选择（policy choices）的组合TMP能够被用来产生个性化的代码，也能够用来防止为特定类型生成不合适的代码。




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 49 理解new-handler的行为
// 1. new-handler介绍
// 当操作符new不能满足内存分配请求的时候，它就会抛出异常。很久之前，它会返回一个null指针，一些旧的编译器仍然会这么做。你仍然会看到这种旧行为，但是我会把关于它的讨论推迟到本条款结束的时候。
// 1.1 调用set_new_handler来指定全局new-handler
// 在operator new由于不能满足内存分配要求而抛出异常之前，它会调用一个客户指定的叫做new-handler的错误处理函数。（这也不是完全正确的。Operator new的真正行为更加复杂。详细内容在Item 51中描述。）
// 为了指定内存溢出处理（out-of-memory-handling）函数，客户可以调用set_new_handler函数，这个标准库函数被声明在<new>中：
namespace std {
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
 

正如你所看到的，new_handler是一个函数指针的typedef，这个函数没有参数没有返回值，set_new_handler是一个参数和返回值都为new_handler的函数。（函数set_new_handler声明结束处的”throw()”是一个异常指定（exception specification）。从本质上来说它的意思是说这个函数不会抛出任何异常，然而事实更加有意思。详细内容见Item 29。）

set_new_handler的参数是指向函数的指针，operator new会在请求的内存无法分配的情况下调用这个函数。Set_new_handler的返回值也是指向函数的指针，返回的是在调用set_new_handler之前调用的new_handler函数（也就是在new_handler被替换之前的函数）。

你可以像下面这样使用set_new_handler：

// function to call if operator new can’t allocate enough memory
void outOfMem()
{
std::cerr << "Unable to satisfy request for memory\n";
std::abort();
}

int main()
{
std::set_new_handler(outOfMem);
int *pBigDataArray = new int[100000000L];
...
}

如果operaotr new无法为100,000,000个整数分配内存，就会调用outOfMem，也就是输出一个error信息之后程序终止（abort）。（顺便说一下，考虑在向cerr中写入error信息期间如果必须动态的分配内存会发生什么。。）

1.2 如何设计一个良好的new-handler函数
当operator new不能满足一个内存请求的时候，它会反复调用new-handler函数直到它发现有足够的内存可以分配了。引起这些函数被反复调用的代码在Item 51中可以找到，但是这种高级别的描述信息足够让我们得出结论：一个设计良好的new-handler函数必须能够做到如下几点。

提供更多的可被使用的内存。这可以保证下次在operator new内部尝试分配内存时能够成功。实现这个策略的一种方法是在程序的开始阶段分配一大块内存，然后在第一次调用new-handler的时候释放它。
安装一个不同的new-handler。如果当前的new-handler不能够为你提供更多的内存，可能另外一个new-handler可以。如果是这样，可以在当前的new-handler的位置上安装另外一个new-handler（通过调用set_new_handler）。下次operator new调用new-handler函数的时候，它会调用最近安装的。（这个主题的一个变种是一个使用new_handler来修改它自己的行为，所以在下次触发这个函数的时候，它就会做一些不同的事情。达到这个目的的一个方法是让new_handler修改影响new-handler行为的static数据,命名空间数据或者全局数据。）
卸载new-handler，也就是为set_new_handler传递null指针。如果没有安装new-handler，operator  new在内存分配失败的时候会抛出异常。
没有返回值，调用abort或者exit。
这些选择让你在实现new-handler的时候有相当大的灵活性。

回到顶部
2. 为特定类指定new-handler
有时候你想用不同方式来处理内存分配失败，这依赖于需要分配内存的对象所属的类：


class X {
public:
static void outOfMemory();
...
};
class Y {
public:
static void outOfMemory();
...
};
X* p1 = new X; // if allocation is unsuccessful,
// call X::outOfMemory
Y* p2 = new Y; // if allocation is unsuccessful,
// call Y::outOfMemory

C++没有为类提供指定的new-handlers，但也不需要。你可以自己实现这种行为。你可以使每个类提供自己版本的set_new_handler和operator new。类中的set_new_handler允许客户为类提供new_handler（就像标准的set_new_handler允许客户指定全局的new-handler一样）。类的operator new确保为类对象分配内存时，会使用其指定的new-handler来替代全局new-handler。

2.1 在类中声明static new_handler成员
假设你想对Widget类对象的内存分配失败做一下处理。当operator new不能为Widget对象分配足够的内存的时候你必须跟踪一下函数调用过程，所以你要声明一个类型为new_handler的static成员，来指向这个类的new-handler函数。Widget将会是下面这个样子：


class Widget {
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};



静态类成员必须在类外部定义（除非他们是const整型，见Item 2）,所以：

1 std::new_handler Widget::currentHandler = 0; // init to null in the class
2 // impl. File
 

Widget中的set_new_handler函数会把传递进去的指针（所指向的new-handler函数）保存起来，并且会返回调用set_new_handler之前所保存的指针。这也是标准版本set_new_handler的做法：

复制代码
1 std::new_handler Widget::set_new_handler(std::new_handler p) throw()
2 {
3 std::new_handler oldHandler = currentHandler;
4 currentHandler = p;
5 return oldHandler;
6 }
复制代码
 

2.2 重新定义operator new
最后，Widget的operator new将会做下面的事情：

调用标准set_new_handler，参数为Widget的错误处理函数。这就将Widget的new-handler安装成为了全局的new-handler。
调用全局的operator new来执行实际的内存分配。如果分配失败，全局的operator new会触发Widget的new-handler，因为这个函数已经被安装为全局new-handler。如果全局的operator new最终不能分配内存，它会抛出bad_alloc异常。在这种情况下，Widget的operator new必须恢复原来的全局new-handler，然后传播异常。为了确保源new-handler总是能被恢复，Widget将全局new-handler作为资源来处理，遵循Item 13的建议，使用资源管理对象来防止资源泄漏。
如果全局operator new能够为Widget对象分配足够的内存。Widget的operator new就会返回指向被分配内存的指针。管理全局new-handler的对象的析构函数会自动恢复调用Widget的operator new之前的new-handler。
这里我们以资源处理（resource-handling）类开始，只包含基本的RAII处理操作，包括在构造时获取资源和在在析构时释放资源（Item 13）:



class NewHandlerHolder {
public:
explicit NewHandlerHolder(std::new_handler nh) // acquire current
: handler(nh) {} // new-handler

~NewHandlerHolder()                             // release it

{ std::set_new_handler(handler); }

private:



std::new_handler handler;                             // remember it

NewHandlerHolder(const NewHandlerHolder&);      // prevent copying



NewHandlerHolder&                                   // (see Item 14)

operator=(const NewHandlerHolder&);

};


 这会使得Widget的operator new的实现非常简单：


 void* Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder // install Widget’s
h(std::set_new_handler(currentHandler)); // new-handler

return ::operator new(size);        // allocate memory
// or throw

}                                                  // restore global
// new-handler



void outOfMem();                                 // decl. of func. to call if mem. alloc.
// for Widget objects fails

Widget::set_new_handler(outOfMem); // set outOfMem as Widget’s
// new-handling function

Widget *pw1 = new Widget;                 // if memory allocation
// fails, call outOfMem

std::string *ps = new std::string;           // if memory allocation fails,
// call the global new-handling
// function (if there is one)

Widget::set_new_handler(0);                // set the Widget-specific
// new-handling function to
// nothing (i.e., null)

Widget *pw2 = new Widget;                 // if mem. alloc. fails, throw an
// exception immediately. (There is
// no new- handling function for
// class Widget.)

2.3 将NewHandlerHolder转换为模板
不管在什么类中，实现的这个主题的代码都是一样的，所以我们可以为其设一个合理的目标，就是代码能够在其他地方重用。达到这个目标的一个简单方法是创建一个“混合风格（mixin-style）”的基类，也就是设计一个基类，允许派生类继承单一特定的能力——在这个例子中，这种能力就是为类指定new-handler。然后将基类变为一个模板，于是你可以为每个继承类获得一份不同的类数据的拷贝。

这个设计的基类部分使得派生类能够继承它们都需要的set_new_handler和operator new函数，同时设计的模板部分确保每个继承类获得一个不同的currentHandler数据成员。说起来有些复杂，但是代码看上去很熟悉。事实上，唯一真正不一样的是现在任何类都能够获得这个功能：


template<typename T> // “mixin-style” base class for
class NewHandlerSupport { // class-specific set_new_handler
public: // support
static std::new_handler set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
... // other versions of op. new —
// see Item 52
private:
static std::new_handler currentHandler;
};
template<typename T>
std::new_handler
NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
template<typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size)
throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::operator new(size);
}
// this initializes each currentHandler to null
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;


有了这个类模板之后，向Widget中添加set_new_handler支持就变得容易了：Widget只需要继承自NewHandlerSupport<Widget>。（这可能看上去比较独特，接下来我会进行详细的解释。）

1 class Widget: public NewHandlerSupport<Widget> {
2 ... // as before, but without declarations for
3 
4 };                             // set_new_handler or operator new
 

这是Widget提供一个特定的set_new_handler需要做的所有事情。

但是对于Widget继承自NewHandlerSupport<Widget>，你可能还是有些不安。如果是这样，当你注意到NewHandlerSupport模板永远不会使用类型参数T之后你的不安可能会加剧。你没有必要这样。对于每个继承自NewHandlerSupport的类来说，我们所有需要的是一份不同的NewHandlerSupport的拷贝——特别是静态数据成员currentHandler的不同拷贝。模板机制自身会为每个T自动生成currentHandler的一份拷贝，NewHandlerSupport使用这个T来进行实例化。

对于Widget继承自一个使用Widget作为类型参数的模板基类来说，如果这个概念让你感觉眩晕，不要感觉不好。每个人看到开始看到它的时候都会有这种感觉。但是，它是非常有用的技术，它有一个名字，这个名字如果这个概念一样，第一次看到它的人没有人会感觉它很自然，它叫做怪异的循环模板模式（curiously recurring template pattern CRTP）。

我曾经写过一遍文章建议为它起一个更好的名字：do it for me，因为当Widget继承自NewHandlerSupport<Widget>，它真的像是在说：“我是Widget，我需要为Widget继承NewHandlerSupport类“。没有人使用我建议的名字，但是使用“do it for me”来想象一下CRTP可能会帮助你理解模板化的继承会做什么。

有了像NewHandlerSupport这样的模板，为任何需要new-hadler的类添加一个特定的new-handler就会变得容易。混合风格的继承总是会将你引入多继承的主题，在开始进入这个主题之前，你可能想读一下Item 40。

回到顶部
3. Nothrow版本的new
直到1993年，当不能满足分配内存的要求时，C++要求operator new要返回null。现在指定operator new要抛出bad_alloc异常，但是大量的C++是在编译器支持修订版本之前写出来的。C++标准委员会也不想废弃test-for-null的代码，所以它们为operator new提供了一种替代形式，它能够提供传统的“失败产生null（failure-yields-null）”行为。这些形式被叫做“nothrow”形式，某种程度上是因为他们使用了不会抛出异常的对象（定义在头文件<new>中），new在这种情况下被使用：


class Widget { ... };
Widget *pw1 = new Widget;                        // throws bad_alloc if
// allocation fails

if (pw1 == 0) ...                                             // this test must fail

Widget *pw2 = new (std::nothrow) Widget;   // returns 0 if allocation for
// the Widget fails

if (pw2 == 0) ...                                             // this test may succeed

nothrow版本的new不会像从表面上看起来这样可靠，对于异常它没有提供让人信服的保证。对于表达式“new (std::nothrow) Widget”，会发生两件事情。首先，通过调用nothrow版本的operator new来为一个Widget 对象分配足够的内存。如果分配失败了，operator new会返回null指针。然而如果分配成功了，Widget构造函数会被调用，到这个时候，就会世事难料了。Widget构造函数能够做任何它想做的。它自己可能new一些内存，如果是这样，并没有强迫它使用nothrow版本的new。虽然在”new (std::nothrow) Widget”中的operator new不会抛出异常，但是Widget构造函数却可能抛出来。如果是这样，异常会像平时一样传播出去。结论是什么？使用nothrow new只能保证operator new不会抛出异常，不能保证像“new(std::nothrow) Widget”这样的表达式不抛出异常。十有八九，你将永远不会有使用nothrow new的需要。

不论你是使用”普通的”（也就是抛出异常的）new还是nothrow版本的new，重要的是你需要明白new-handler的行为，因为在两种new中都会使用到它。

回到顶部
4. 总结
Set_new_handler允许你在分配内存不能满足要求的时候指定一个特定的被调用的函数。
Nothrow new功能有限，因为它只能被应用在内存分配上；相关联的构造函数调用可能仍然会抛出异常。


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 50 了解何时替换new和delete 是有意义的



1. 自定义new和delete的三个常见原因
我们先回顾一下基本原理。为什么人们一开始就想去替换编译器提供的operator new和operator delete版本？有三个最常见的原因：

为了检测内存使用错误。不能成功delete new出来的内存会造成内存泄漏。在new出来的内存上使用多于一次的delete会产生未定义行为。如果operator new持有一份内存分配的列表，并且operator delete从列表中移除地址，那么就很容易侦测出这种使用错误。类似的，不同种类的编程错误能够导致数据右越界（overrun）（越过分配内存块的结尾写数据）或者左越界(underrun)（在分配内存块的开始之前写数据）。自定义的operator new能够分配额外的内存块，所以在客户申请内存前后就有空间存放已知的字节模式（“签名signatures”）。Operato delete能够检查签名是否发生了变化，如果变了，那么在分配内存块的生命周期中，越界（overrun orunderrun）就有可能会发生,operator delete会记录这个事实，并且将违规指针的值记录下来。
为了提高效率。编译器提供的operator new和operator delete的版本是供大众使用的。它们必须能被长时间运行的程序所用（例如 web server），也能被执行时间小于1秒的程序所使用。它们必须要处理对大内存块，小内存块以及大小混合内存块的请求。它们必须要适应不同的内存分配模式，从为持续运行的程序提供内存块的动态分配到为大量短暂存在对象提供的常量大小的内存块分配和释放。它们必须考虑内存碎片问题，如果不做内存碎片的检查，最后有可能发生内存充足却因为分布在不同的小内存块中而导致内存请求失败的问题。
考虑以上在内存管理上的不同要求，编译器版本的operator new和operator delete为你提供一个大众化内存分配策略就不足为奇了。它们能够为每个人都工作  的很好，但是对于这些人来说都不是最优的。如果你对程序的动态内存运用模式有一个很好的理解，你就会发现使用自定义版本的operator new和operator delete会胜过默认版本。“胜过”的意思就是它们运行的更快——有时速度提升是数量级的,它们使用的内存会更少——最高能减少50%的内存。对于一些应用来说，能够很容易的替换默认operator new和operator delete版本，却能够收获很大的性能提升。

为了收集内存使用的统计信息。在沿着自定义new和delete这条小路前进之前，对你的软件是如何使用动态分配内存的相关信息进行收集是很精明的。内存分配块的大小是如何分布的？内存块的生命周期是如何分布的？内存的分配和释放是使用FIFO（先进先出）的顺序，还是使用LIFO（后进先出）的顺序？或者有时候更加趋近于随机的顺序？内存使用的模式是不时地发生变化的么？例如，你的软件在不同的执行阶段是不是有不同的内存分配和释放模式？一次能够使用的动态分配内存的最大容量是多少？自定义版本的operator new和operator delete使得收集这些信息变得容易。
回到顶部
2. 自定义operator new中的对齐问题
从概念上来说，实现一个自定义operator new是非常容易的。例如，我们快速的实现一个全局operator new，它能够很容易的检测内存越界。它也有很多小的错误，但是我们一会再去为它们担心。

static const int signature = 0xDEADBEEF;

typedef unsigned char Byte;
// this code has several flaws — see below
void* operator new(std::size_t size) throw(std::bad_alloc)
{

using namespace std;
size_t realSize = size + 2 * sizeof(int);             // increase size of request so 2
// signatures will also fit inside

void *pMem = malloc(realSize);   // call malloc to get the actual



if (!pMem) throw bad_alloc();                                                          // memory

// write signature into first and last parts of the memory

*(static_cast<int*>(pMem)) = signature;

*(reinterpret_cast<int*>(static_cast<Byte*>(pMem)+realSize-sizeof(int))) =

signature;

// return a pointer to the memory just past the first signature

return static_cast<Byte*>(pMem) + sizeof(int);

}

这个operator new的大多数毛病是因为它不符合C++惯例。例如，Item 51中解释了所有的operator new都应该包含一个反复调用new-handling函数的循环，但是这个函数里没有。然而，因为在Item51中会有解释，在这里我们将其忽略。我现在想关注一个更加微妙的问题：对齐（alignment）。

对于许多计算机架构来说，在内存中替换特定类型的数据时，需要在特定种类的地址上进行。例如，一种架构可能需要指针定义的开始地址为4的整数倍（也就是4字节对齐的）或者定义double的开始地址必须为8的整数倍（也就是8字节对齐的）。不遵守这个约束条件在运行时就会导致硬件异常。其他架构可能更加宽松，也即是如果满足对齐会有更好的性能。例如，在英特尔X86架构中double可以被对齐在任何字节边界上，但是如果它们是8字节对齐的，访问它们的速度会大大加快。

Operator new和对齐（alignment）是相关的，因为C++需要所有operator new返回的指针都能够被恰当的对齐，malloc工作在同样的需求下，所以让operator new返回从malloc得到的指针是安全的。然而，在上面的operator new中，我们没有返回从malloc得到的指针，我们返回的是从malloc得到的指针加上int大小的偏移量。这就在安全上没有保证了！如果客户通过调用operator new来为double获取足够的内存（或者如果我们实现了operator new[]，为double数组申请内存），并且我们工作在int为4字节大小但是double需要8字节对齐的机器上，我们可能返回一个没有恰当的对齐的指针。这可能会导致程序崩溃。或者它只会导致程序运行更加缓慢。不管哪种结果，都不是我们想要的。

回到顶部
3. 通常情况你无需自定义new和delete
因为像对齐（alignment）这样的细节问题的存在，程序员在专心完成其他任务的时候将这些细节问题忽略会导致各种问题的抛出，这就能够将专业级别的内存管理器区分出来。实现一个能够工作的内存管理器是非常容易的。实现一个工作良好的就非常难了。作为通用规则，我建议你不要尝试，除非有必要。

3.1 使用默认版本和商业产品
在许多情况下，你不必这么做。在一些编译器的内存管理函数中有控制调试和记录日志功能的开关。快速瞥一眼你的编译器文档可能就能消除你自己来实现New和delete的想法。在许多平台中，商业产品能够替换编译器自带的内存管理函数。它们的增强的功能和改善的性能能够使你受益，你所需要做的就是重新链接（前提是你必须买下这个产品。）

3.2 使用开源内存管理器
另外一个选择是开源的内存管理器。在许多平台上都能找到这样的管理器，所以你可以下载和尝试。其中一个开源的内存分配器是来自Boost的Pool库（Item 55）。这个Pool库提供的内存分配器对自定义内存管理很有帮助：也就是在有大量的小对象需要分配的时候。许多C++书籍中，包含本书的早期版本，展示出了高性能小对象内存分配器的源码，但他们通常都会忽略一些细节，像可移植性，对于对齐的考虑，线程安全等等。真正的库提供的源码都是更加健壮的。即使你自己决定去实现你自己的new和delete，看一下这些开源的版本能够让你对容易忽略的细节有了深刻洞察力，而这些细节就将“基本工作”和“真正工作”区分开来。（鉴于对齐是这样一个细节，因此注意一下TR1是很有价值的，其中包含了对特定类型对齐的支持。）

回到顶部
4. 使用自定义版本new和delete的意义总结
这个条款的论题是让你知道在什么情况下对默认版本的new和delete进行替换是有意义的，无论是在全局范围内替换还是在类的范围内替换。我们现在做一个总结。

检测内存使用错误。
收集使用动态分配内存的统计信息。
提高内存分配和释放的速度。为大众提供的分配器通常情况下比自定义版本要慢的多，特别是在自定义版本是专门为特定类型对象所设计的情况下。类特定的分配器是固定大小分配器的一个实例应用，例如在Boost的Pool库中提供的分配器。如果你的应用是单线程的，但是你的编译器默认版本是线程安全的，你可以通过实现线程不安全的分配器来获得可观的速度提升。当然，在下决定要提升operator new和operator delete的速度之前，研究一下你的程序来确定这些函数真的是瓶颈所在。
减少默认内存管理的空间开销。大众内存管理器通常情况下不仅慢，而且使用更多的内存。因为它们会为每个内存分配块引入一些额外的开销。为小对象创建的分配器从根本上消除了这些开销。
能够补偿在默认分配器中的次优对齐。正如我先前提到的，在X86架构的机器上访问double，在8字节对齐的情况下速度是最快的。但是一些编译器中的operator new不能够保证对于动态分配的double是8字节对齐的。在这种情况中，用能够保证8字节对齐的版本替换默认版本可以很大程度的提高性能。
将相关对象集中起来。如果你知道一些特定的数据结构通常情况下会被放在一起被使用，当在这些数据上进行工作时你想让页错误出现的频率最小化，为这些数据结构创建一个单独的堆就有意义了，这样它们就能够聚集在尽可能少的页中。替换new和delete的默认版本可以达到这种聚集。
可以获得非常规的行为。有时候你想让operator new和delete能够做一些编译器版本不能做的事。例如，你可能想在共享内存中进行内存分配和释放，但是你只有一个C API来进行内存管理。实现自定义版本的new 和delete（可能是placement 版本——见Item 52）允许你为C API穿上C++的外衣。另外一个例子，你可以自己实现一个operator delete来为释放的内存填充数据0以达到增强应用数据安全性的目的。
回到顶部
5. 本条款总结
有许多正当的理由来自定义new 和delete，包括提高性能，调试堆应用错误和收集堆使用信息。



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Item 51 实现new和delete的时候要遵守约定

Item 50中解释了在什么情况下你可能想实现自己版本的operator new和operator delete，但是没有解释当你实现的时候需要遵守的约定。遵守这些规则并不是很困难，但是它们其中有一些并不直观，所以知道这些规则是什么很重要。

回到顶部
1. 定义operator new的约定
1.1 约定列举
我们以operator new开始。实现一个一致的operator new需要有正确的返回值，在没有足够内存的时候调用new-handling函数（见Item 49）,并且做好准备处理没有内存可分配的情况。你也想避免无端的隐藏“正常”版本的new，但这是一个类接口的问题而不是实现需求问题；它会在Item 52中进行处理。

Operator new的返回值部分很简单，因为operator new事实上会尝试多次分配内存，在内次分配失败之后都会调用new-handling函数。这里的假设是new-handling函数可能会做一些事情来释放一些内存。只有在指向new-handling函数的指针为null的情况下，operator new才会抛出异常。

好奇的是，C++即使在请求0个byte的时候也需要operator new返回一个合法的指针。（这个听上去很奇怪的要求简化了语言中的某些事情。）这就是基本情况，一个非成员operator new的伪代码会是像下面这个样子：


void* operator new(std::size_t size) throw(std::bad_alloc)
{ // your operator new might

using namespace std;             // take additional params

if (size == 0) {          // handle 0-byte requests


size = 1; // by treating them as
} // 1-byte requests
while (true) {
attempt to allocate size bytes;

if (the allocation was successful)
return (a pointer to the memory);
// allocation was unsuccessful; find out what the
// current new-handling function is (see below)
new_handler globalHandler = set_new_handler(0);
set_new_handler(globalHandler);
if (globalHandler) (*globalHandler)();
else throw std::bad_alloc();
}
}

把请求0个byte当作请求1一个byte来进行处理的诡计看上去让人厌恶，但这是简单的实现并且合法，而且能够工作，无论如何，你对0个byte的请求会有多频繁呢？

对于伪代码中将new-handling函数指针设为null，然后迅速的将其复原的地方，你可能看上去比较怀疑。不幸的是，没有其他方法直接获得new-handling函数的指针，所以你必须调用set_new_handler来发现这个函数是什么。看上去粗糙但却是有效的，起码对于单线程来说是有效的。在多线程环境中，你可能需要某种类型的锁来安全的操作new-handling函数背后的（全局）数据结构。

在Item 49中讨论过了，在operator new中包含一个无限循环，上面的代码中将其展示了出来；“while(true)”就 表示一个无限循环。跳出循环的唯一方法是成功的分配内存或者让new-handling函数做到Item 49中描述的事情中的其中一件：有更多的内存可供分配，安装一个不同的new-handler，卸载new-handler，抛出一个异常，这个异常要么继承自bad_alloc要么源于失败返回。现在你应该清楚为什么new-handler必须做到这些事情中的一件的了，如果做不到，operator new中的循环永远不会终止。

1.2 由继承导致的问题
许多人没有意识到operator new成员函数是要被派生类继承的。这可能会导致一些有趣的并发症。在上面的operator new的伪代码中，注意函数尝试分配size个bytes。这再合理不过了，因为这是传递到函数中的参数。然而，正如Item 50中解释的，实现一个自定义内存管理器的最一般的原因就是为特定类的对象进行内存分配优化，而不是为类或者它的任何派生类。也即是，我们为类X提供了一个operaor new，这个函数的行为是为大小正好为sizeof(X)的对象进行调整，即不大也不小。然而由于继承的存在，可能发生通过调用基类中的operator new来为派生类对象分配内存：


class Base {
public:
static void* operator new(std::size_t size) throw(std::bad_alloc);
...
};
class Derived: public Base // Derived doesn’t declare
{ ... }; // operator new

Derived *p = new Derived;                             // calls Base::operator new!

如果基类中的operator new设计没有处理这种情况，处理它的最好的方法将对“错误”数量内存的请求丢弃掉，而是转而使用标准operator new来处理,就像下面这样：

void* Base::operator new(std::size_t size) throw(std::bad_alloc)

{


if (size != sizeof(Base)) // if size is “wrong,”
return ::operator new(size); // have standard operator
// new handle the request
... // otherwise handle
// the request here
}

“等一下”我听见你大叫，“你忘记检查病态但是可能发生的情况，也就是size为0的情况了！”事实上，我没有忘记。测试仍然在那里，只不过是将测试并入size同sizeof(size)的测试之中了。C++用神秘的方式进行工作，其中之一的方式就是规定所有独立对象的大小不能为0（见Item 39）。根据定义，sizeof(Base)永远不会为0，所以如果size为0，内存请求将由::operator new来处理，它会以一种合理的方式来处理这个请求。

 

1.3 定义operator new[]的约定
 

如果你想在一个类中控制数组的内存分配，你需要实现operator new的数组形式，operator new[]。（这个函数通常被叫做“array new”，因为很难确定“operator new[]”该如何发音）。如果你决定实现operator new[],记住所有你正在做的是分配一大块原生内存——你不能对不存在于数组中的对象做任何事情。事实上，你甚至不能确定数组将会有多少对象。首先，你不会知道每个对象有多大。毕竟，很有可能通过继承来调用基类的operator new[]去为派生类对象数组分配内存，派生类对象通常比基类对象要大。因此，你不能假设在Base::operator new[]内部被放入数组的对象的大小为sizeof(Base)，这就意味着你不能假设数组中对象的数量为(请求的字节数)/sizeof(Base)。第二，传递给operator new[]的参数size_t有可能比填入对象的内存更多，因为正如Item 16中解释的，动态分配的数组有可能包含额外的空间来存放数组元素的数量。

 

回到顶部
2. 定义operator delete的约定
 

当实现operator new的时候需要遵守的约定就这么多。对于operator delete,事情更加简单。所有你需要记住的是C++总是保证delete null指针是安全的，所以你需要遵守这个规定。下面是实现非成员 operator delete的伪代码：

void operator delete(void *rawMemory) throw()
{
if (rawMemory == 0) return; // do nothing if the null
// pointer is being deleted
deallocate the memory pointed to by rawMemory;
}

这个函数的成员函数版本也是简单的，但是你需要确保检查正在被delete的对象的size。假设你的属于类的operator new将对错误数量内存的请求转发给了::operator new，你同样得将对“错误大小”的delete请求转发给::operator delete:


class Base { // same as before, but now
public: // operator delete is declared
static void* operator new(std::size_t size) throw(std::bad_alloc);
static void operator delete(void *rawMemory, std::size_t size) throw();
...
};
void Base::operator delete(void *rawMemory, std::size_t size) throw()
{

if (rawMemory == 0) return; // check for null pointer

if (size != sizeof(Base)) { // if size is “wrong,”


::operator delete(rawMemory); // have standard operator

return;                                                                           // delete handle the request

}

deallocate the memory pointed to by rawMemory;

return;

}

 有趣的是，如果要被delete的对象派生自于一个没有虚析构函数的基类，那么传递给operator delete的size_t值有可能是不正确的。这就有了足够的理由来把你的基类中的析构函数声明为虚函数，但是Item 7中描述了第二个可能更好的原因。现在你需要注意的是如果你在基类中忽略了虚析构函数，operator delete函数的工作就有可能不正确。

 

回到顶部
3. 总结
 operator new应该包含一个无限循环来尝试分配内存，如果不能满足对内存的请求应该调用new-handler,应该处理对0个byte的请求。类的特定版本应该处理比预期更大的内存块的请求。
operator delete中传递的指针如果是null，应该什么都不做。类特定版本需要处理比预期要大的内存块。



/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 52 如果你实现了placement new,你也要实现placement delete
// 1. 调用普通版本的operator new抛出异常会发生什么？
// Placement new和placement delete不是C++动物园中最常遇到的猛兽，所以你不用担心你对它们不熟悉。当你像下面这样实现一个new表达式的时候，回忆一下Item 16和Item 17：
Widget *pw = new Widget;
// 两个函数会被调用：一个是调用operator new来分配内存，第二个是Widget的默认构造函数。
// 假设第一个调用成功了，但是调用第二个函数抛出了异常。在这种情况下，对步骤一中执行的内存分配必须进行回滚。否则就会发生内存泄漏。
// 客户端代码不能释放内存，因为如果Widget构造函数抛出了异常，pw永远不会赋值。客户端就没有办法得到指向需要释放内存的指针。对步骤一进行回滚的责任就落在了C++运行时系统身上。
// 运行时系统很高兴去调用与步骤1中调用的operator new版本相对应的operator delete，但是只有在它知道哪个operator delete（可能有许多）是合适的被调用函数的情况下才能做到。
// 如果你正在处理的new和delete版本有着正常的签名，那么这不是一个问题，因为正常的operator new，

void* operator new(std::size_t) throw(std::bad_alloc);
// 对应着正常的operator delete:

void operator delete(void *rawMemory) throw();     // normal signature at global scope
void operator delete(void *rawMemory, std::size_t size) throw();   // typical normal signature at class  scope        
// 2. 调用自定义operator new抛出异常会发生什么？
// 2.1 一个有问题的例子
// 如果你正在使用普通形式的new和delete,运行时系统能够找到new对应版本的delete来执行回滚操作。然而，如果你开始声明非普通版本的new——也就是生成一个带参数的版本，“哪个delete才是new对应的版本”这个问题就出现了。

// 例如，假设你实现了一个类特定版本的operator new，它需要指定一个ostream来为内存分配信息进行记录，你同样实现了一个普通的类特定版本的operator delete:
class Widget {
public:
static void* operator new(std::size_t size, std::ostream& logStream) // non-normal form of new
throw(std::bad_alloc);
static void operator delete(void *pMemory,std::size_t size) throw();  // normal class specific form of delete
};

// 2.2 对相关术语的说明
// 这个设计是有问题的，但是在我们讨论原因之前，我们需要对相关术语进行说明。
// 当一个operator new函数带了额外的参数（除了必须要带的size_t参数）的时候，我们知道这是new的placement版本。上面的operator new就是这样一个placement版本。
// 一个尤为有用的placement new是带有一个指针参数，指定对象应该在哪里被构建。它会像下面这个样子：

void* operator new(std::size_t, void *pMemory) throw(); // “placement new”
// 这个版本的new是C++标准库的一部分，只要你#inlucde <new>就能够访问它。它也用来在vector的未被使用的空间中创建对象。它还是最早的placement new。事实上，这也是这个函数的命名依据：在特定位置上的new。
// 这就意味着“placement new”被重载了。大多情况下当人们谈到placement new的时候，它们讨论的是这个特定的函数，也即是带有一个void *额外参数的operator new。
// 少数情况下，它们讨论的是带有额外参数的任意版本的operator new。程序的上下文往往会清除这种模棱两可，但是明白普通术语“placement new”意味着带额外参数的任意new版本是很重要的事，因为“placement delete”(我们一会会碰到)直接派生自它。
// 2.3 如何解决问题
// 现在让我们回到对Widget 类的声明上来，我在前面说过这个设计是有问题的。难点在于这个类会发生微妙的内存泄漏。考虑下面的客户代码，在动态创建一个Widget的时候它将内存分配信息记录到cerr中：

Widget *pw = new (std::cerr) Widget; // call operator new, passing cerr as the ostream; this leaks memory if the Widget constructor throws
// 还是上次的问题，当内存分配成功了，但是Widget构造函数抛出了异常，运行时系统有责任将operator new执行的分配工作进行回滚。然而，运行时系统不能够真正明白被调用的operator new版本是如何工作的，
// 所以它不能够自己进行回滚操作。相反，运行时系统会寻找一个operator delete，它和operator new带有相同数量和类型的额外参数，如果找到了，那么这个就是它要调用的版本。
// 在上面的例子中，operator new带有一个额外的参数ostream&,所以对应的operator delete就是：
void operator delete(void*, std::ostream&) throw();

// 同new的placement 版本进行对比，带有额外参数的operator delete版本被叫做placement delete。在这种情况下，Widget没有声明operator delete的placement 版本，所以运行时系统不知道如何对placement new的操作进行回滚。
// 因此它不会做任何事情。在这个例子中，如果Widget构造函数抛出异常之后没有operator delete会被调用！
// 规则很简单：如果一个带了额外的参数operator new 没有与之相匹配的带有相同额外参数的operator delete版本，如果new的内存分配操作需要被回滚那么没有operator delete会被调用。
// 为了消除上面代码的内存泄漏，Widget需要声明一个与记录日志的placement new版本相对应的placement delete:

class Widget {
public:
static void* operator new(std::size_t size, std::ostream& logStream)
throw(std::bad_alloc);
static void operator delete(void *pMemory) throw();
static void operator delete(void *pMemory, std::ostream& logStream)
throw();
};
// 有了这个改动，在下面的语句中，如果异常从Widget构造函数中抛出来：
Widget *pw = new (std::cerr) Widget; // as before, but no leak this time
// 对应的placement delete会被自动被调用，这就让Widget确保没有内存被泄漏。
// 3. 调用delete会发生什么？
// 然而，考虑如果没有异常被抛出的时候会发生什么，我们会在客户端代码中进行delete:
delete pw; // invokes the normal operator delete
// 正如注释所说明的，这会调用普通的operator delete，而不是placement 版本。Placement delete只有在构造函数中调用与之相匹配的placement new时抛出异常的时候才会被触发。
// 对一个指针使用delete（就像上面的pw一样）永远不会调用delete的placement版本。
// 这就意味着为了对new的placement 版本造成的内存泄漏问题进行先发制人，你必须同时提供operator delete的普通版本（在构造期间没有异常抛出的时候调用），以及和placement new带有相同额外参数的placement版本（抛出异常时调用）。
// 做到这一点，在内存泄漏的微妙问题上你就永远不需要在辗转反侧难以入睡了。

// 4. 注意名字隐藏问题
// 顺便说一下，因为成员函数名字会隐藏外围作用域中的相同的名字（见Item 33）,你需要小心避免类特定的new版本把客户需要的其他版本隐藏掉（包括普通版本）。例如如果你有一个基类只声明了一个operator new的placement 版本，
// 客户将会发现它们不能再使用new的普通版本了：
class Base {
public:
static void* operator new(std::size_t size, std::ostream& logStream) // this new hides the normal global forms
throw(std::bad_alloc); 
};

Base *pb = new Base;                  // error! the normal form of operator new is hidden
Base *pb = new (std::cerr) Base; // fine, calls Base’s placement new
// 类似的，派生类中的operator new会同时把operator new的全局版本和继承版本隐藏掉：

class Derived: public Base {         // inherits from Base above
public:
static void* operator new(std::size_t size) 
throw(std::bad_alloc); // redeclares the normal form of new
};
Derived *pd = new (std::clog) Derived; // error! Base’s placement new is hidden
Derived *pd = new Derived; // fine, calls Derived’s operator new

// Item 33中非常详细的讨论了这种类型的名字隐藏，但是为了实现内存分配函数，你需要记住的是默认情况下，C++在全局范围内提供了如下版本的operator new:
void* operator new(std::size_t) throw(std::bad_alloc);          // normal new
void* operator new(std::size_t, void*) throw();    // placement new
void* operator new(std::size_t,                             // nothrow new —
const std::nothrow_t&) throw(); // see Item 49
// 如果你在类中声明了任何operator new，你就会隐藏这些标准版本。除非你的意图是防止客户使用这些版本，否则除了任何你所创建的自定义operator new版本之外，确保这些标准版本能够被客户所用。
// 对每个你所提供的operator new，确保同时提供相对应的operator delete。如果你想让这些函数的行为同普通函数一样，让你的类特定版本调用全局版本就可以了。
// 实现这个目的的一种简单的方法是创建一个包含所有new 和delete版本的基类：

class StandardNewDeleteForms {
public:
// normal new/delete
static void* operator new(std::size_t size) throw(std::bad_alloc)
{ return ::operator new(size); }
static void operator delete(void *pMemory) throw()
{ ::operator delete(pMemory); }

// placement new/delete
static void* operator new(std::size_t size, void *ptr) throw()
{ return ::operator new(size, ptr); }
static void operator delete(void *pMemory, void *ptr) throw()
{ return ::operator delete(pMemory, ptr); }
// nothrow new/delete
static void* operator new(std::size_t size, const std::nothrow_t& nt) throw()
{ return ::operator new(size, nt); }
static void operator delete(void *pMemory, const std::nothrow_t&) throw()
{ ::operator delete(pMemory); }
};

// 客户如果想在自定义版本的基础上增加标准版本，只需要继承这个基类然后使用using声明就可以(Item 33）获得标准版本：
class Widget: public StandardNewDeleteForms {       // inherit std forms
public:
using StandardNewDeleteForms::operator new; // make those
using StandardNewDeleteForms::operator delete;    // forms visible
static void* operator new(std::size_t size, std::ostream& logStream) // add a custom placement new
throw(std::bad_alloc);
static void operator delete(void *pMemory, std::ostream& logStream)  // add the corres ponding place ment delete
throw(); 
};

// 5. 总结
// 当你实现operator new的placement版本的时候，确保实现与之相对应的operator delete placement版本。如果你不进行实现，有的程序会发生微妙的，间歇性的内存泄漏。
// 当你声明new和delete的placement版本的时候，确保不要无意间隐藏这些函数的普通版本。




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Item 53 关注编译器发出的警告
// 许多程序员常常忽略编译器发出的警告。毕竟，如果问题很严重，它才将会变成一个error，不是么?相对来说，这个想法可能在其它语言是无害的，但是在C++中，我敢打赌编译器的实现者对于对接下来会发生什么比你有更好的理解。
// 例如，下面的错误是每个人都时不时会犯的：

 class B {
 public:
 virtual void f() const;
 };
 class D: public B {
 public:
 virtual void f();
 };

// 你对警告的理解可能是错的
// 你的想法是用D::f重新定义虚函数B::f，但是这里有一个错误：在B中，f是const成员函数，但是在D中，f没有被声明为const。我知道的一个编译器会发出如下警告：
// warning: D::f() hides virtual B::f()
// 太多没有经验的程序员把上面的信息理解为，“当然是D::f把B::f隐藏起来了——这也是它想做的！”但这个理解是错误的。这个编译器正尝试告诉你在B中声明的f没有在D中被重新声明
// 相反，它被完全隐藏起来了（Item 33中描述了为什么会是这样）。忽略这个编译器警告几乎肯定会导致错误的行为，接下来你会花费大量的时间进行调试来发现编译器已经发现的问题。

// 2. 忽略警告之前确保你已经理解其意义
// 在你对特定编译器的警告信息有了一些经验之后，你就会理解不同的信息意味着什么（通常情况下实际的意义和表面上看起来相比会有很大不同。）虽然写出没有编译器警告（即使在最高级别警告下）的程序会是更好的实践，一旦你有了经验，你可能就会选择忽略许多警告。
// 无论如何，在你忽略一个警告之前，确保你已经完全理解了编译器在向你传达什么信息，这很重要。

// 3. 警告信息因编译器而异
// 警告信息和编译器是相关的，所以粗心对待你的代码，而依靠编译器来指出你的错误并不可取。例如，前面提到的函数隐藏的代码，换一个编译器可能没有任何回应。

// 4. 总结
// 认真对待编译器警告，在编译器支持的最大警告级别下努力写出没有警告的程序。
// 不要依赖于编译器警告，因为不同的编译器警告会为不同的事情发出警告。将程序移植到一个新的编译器上可能会消除之前你依赖的警告信息。



int main()
{

    processWidget(pw); // this call won’t leak

    // const CTextBlock cctb("hello");
    // char *pc = &cctb[0];
    // char tmp = 'j';
    // *pc = tmp;

    // 默认构造函数
    Empty e1; // default constructor; destructor
    // 拷贝构造函数
    Empty e2(e1); // copy constructor
    // 拷贝赋值函数
    e2 = e1; // copy assignment operator

    // NamedObject<int> no1("Smallest Prime Number", 2);
    // // 拷贝构造函数
    // NamedObject<int> no2(no1); // calls copy constructor

    HomeForSale h1;
    HomeForSale h2;
    HomeForSale h3(h1); // attempt to copy h1 — should not compile!
                        // h1 = h2; // attempt to copy h2 — should not compile!
    h1 = h2;

    // std::cout<<"cctb[0]  "<< cctb[0] <<std::endl;
    cout << "hello world" << endl;
    return 0;
}